REF: migrate SegmentedControl to New Architecture
This commit is contained in:
parent
038cabedaf
commit
e098e89dc3
@ -102,6 +102,7 @@ android {
|
||||
sourceSets {
|
||||
main {
|
||||
assets.srcDirs = ['src/main/assets', 'src/main/res/assets']
|
||||
java.srcDirs = ['src/main/java', '../../blue_modules/Views/SegmentedControl/android']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil
|
||||
import io.bluewallet.bluewallet.components.segmentedcontrol.CustomSegmentedControlPackage
|
||||
import io.bluewallet.bluewallet.components.segmentedcontrol.SegmentedControlPackage
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
@ -69,7 +69,7 @@ class MainApplication : Application(), ReactApplication {
|
||||
PackageList(this).packages.apply {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// add(MyReactNativePackage())
|
||||
add(CustomSegmentedControlPackage())
|
||||
add(SegmentedControlPackage())
|
||||
add(SettingsPackage())
|
||||
}
|
||||
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
package io.bluewallet.bluewallet.components.segmentedcontrol
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.common.MapBuilder
|
||||
import com.facebook.react.uimanager.SimpleViewManager
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.viewmanagers.CustomSegmentedControlManagerInterface
|
||||
|
||||
@ReactModule(name = CustomSegmentedControlManager.REACT_CLASS)
|
||||
class CustomSegmentedControlManager : SimpleViewManager<CustomSegmentedControl>(),
|
||||
CustomSegmentedControlManagerInterface<CustomSegmentedControl> {
|
||||
|
||||
companion object {
|
||||
const val REACT_CLASS = "CustomSegmentedControl"
|
||||
private const val TOP_CHANGE = "topChange"
|
||||
private const val REGISTRATION_NAME = "onChange"
|
||||
}
|
||||
|
||||
override fun getName(): String = REACT_CLASS
|
||||
|
||||
override fun createViewInstance(reactContext: ThemedReactContext): CustomSegmentedControl {
|
||||
return CustomSegmentedControl(reactContext)
|
||||
}
|
||||
|
||||
@ReactProp(name = "values")
|
||||
override fun setValues(view: CustomSegmentedControl, values: ReadableArray?) {
|
||||
val valuesArray = values?.let { array ->
|
||||
Array(array.size()) { index ->
|
||||
array.getString(index) ?: ""
|
||||
}
|
||||
} ?: emptyArray()
|
||||
|
||||
view.values = valuesArray
|
||||
}
|
||||
|
||||
@ReactProp(name = "selectedIndex", defaultInt = 0)
|
||||
override fun setSelectedIndex(view: CustomSegmentedControl, selectedIndex: Int) {
|
||||
view.selectedIndex = selectedIndex
|
||||
}
|
||||
|
||||
@ReactProp(name = "backgroundColor")
|
||||
override fun setBackgroundColor(view: CustomSegmentedControl, value: String?) {
|
||||
view.setBackgroundColorProp(value)
|
||||
}
|
||||
|
||||
@ReactProp(name = "tintColor")
|
||||
override fun setTintColor(view: CustomSegmentedControl, value: String?) {
|
||||
view.setTintColorProp(value)
|
||||
}
|
||||
|
||||
@ReactProp(name = "textColor")
|
||||
override fun setTextColor(view: CustomSegmentedControl, value: String?) {
|
||||
view.setTextColorProp(value)
|
||||
}
|
||||
|
||||
@ReactProp(name = "momentary", defaultBoolean = false)
|
||||
override fun setMomentary(view: CustomSegmentedControl, value: Boolean) {
|
||||
view.setMomentaryProp(value)
|
||||
}
|
||||
|
||||
@ReactProp(name = "enabled", defaultBoolean = true)
|
||||
override fun setEnabled(view: CustomSegmentedControl, value: Boolean) {
|
||||
view.setEnabledProp(value)
|
||||
}
|
||||
|
||||
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any>? {
|
||||
return MapBuilder.builder<String, Any>()
|
||||
.put(
|
||||
TOP_CHANGE,
|
||||
MapBuilder.of(
|
||||
"phasedRegistrationNames",
|
||||
MapBuilder.of("bubbled", REGISTRATION_NAME, "captured", "${REGISTRATION_NAME}Capture")
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onAfterUpdateTransaction(view: CustomSegmentedControl) {
|
||||
super.onAfterUpdateTransaction(view)
|
||||
}
|
||||
}
|
||||
@ -9,13 +9,13 @@ import androidx.core.content.ContextCompat
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import com.facebook.react.uimanager.events.Event
|
||||
import com.facebook.react.uimanager.UIManagerHelper
|
||||
import com.facebook.react.uimanager.events.Event
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import io.bluewallet.bluewallet.R
|
||||
|
||||
class CustomSegmentedControl @JvmOverloads constructor(
|
||||
class SegmentedControl @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
@ -48,10 +48,13 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
isSingleSelection = true
|
||||
isSelectionRequired = true
|
||||
}
|
||||
addView(toggleGroup, LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
))
|
||||
addView(
|
||||
toggleGroup,
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
),
|
||||
)
|
||||
|
||||
toggleGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||
if (isChecked) {
|
||||
@ -69,29 +72,29 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
|
||||
private fun updateSegments() {
|
||||
toggleGroup.removeAllViews()
|
||||
|
||||
|
||||
values.forEachIndexed { index, title ->
|
||||
val button = MaterialButton(
|
||||
context,
|
||||
null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle,
|
||||
).apply {
|
||||
text = title
|
||||
id = generateViewId()
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
1f
|
||||
1f,
|
||||
)
|
||||
isCheckable = true
|
||||
|
||||
|
||||
strokeWidth = 2
|
||||
applyEnabledState()
|
||||
|
||||
|
||||
val cornerRadius = resources.getDimensionPixelSize(
|
||||
com.google.android.material.R.dimen.mtrl_btn_corner_radius
|
||||
com.google.android.material.R.dimen.mtrl_btn_corner_radius,
|
||||
)
|
||||
|
||||
|
||||
when {
|
||||
values.size == 1 -> {
|
||||
this.cornerRadius = cornerRadius
|
||||
@ -107,10 +110,10 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toggleGroup.addView(button)
|
||||
}
|
||||
|
||||
|
||||
updateButtonColors()
|
||||
updateSelectedSegment()
|
||||
}
|
||||
@ -118,7 +121,7 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
private fun updateButtonColors() {
|
||||
for (i in 0 until toggleGroup.childCount) {
|
||||
val button = toggleGroup.getChildAt(i) as? MaterialButton ?: continue
|
||||
|
||||
|
||||
val selectedBgColor = tintColorProp ?: ContextCompat.getColor(context, R.color.button_background_color)
|
||||
val unselectedBgColor = backgroundColorProp ?: ContextCompat.getColor(context, R.color.button_disabled_background_color)
|
||||
val resolvedTextColor = textColorProp ?: ContextCompat.getColor(context, R.color.button_text_color)
|
||||
@ -127,39 +130,39 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
val borderColor = ContextCompat.getColor(context, R.color.form_border_color)
|
||||
val rippleColor = ContextCompat.getColor(context, R.color.ripple_color)
|
||||
val rippleColorSelected = ContextCompat.getColor(context, R.color.ripple_color_selected)
|
||||
|
||||
|
||||
val bgColorStateList = ColorStateList(
|
||||
arrayOf(
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked)
|
||||
intArrayOf(-android.R.attr.state_checked),
|
||||
),
|
||||
intArrayOf(selectedBgColor, unselectedBgColor)
|
||||
intArrayOf(selectedBgColor, unselectedBgColor),
|
||||
)
|
||||
|
||||
|
||||
val textColorStateList = ColorStateList(
|
||||
arrayOf(
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked)
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked),
|
||||
),
|
||||
intArrayOf(selectedTextColor, unselectedTextColor)
|
||||
intArrayOf(selectedTextColor, unselectedTextColor),
|
||||
)
|
||||
|
||||
|
||||
val strokeColorStateList = ColorStateList(
|
||||
arrayOf(
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked)
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked),
|
||||
),
|
||||
intArrayOf(borderColor, borderColor)
|
||||
intArrayOf(borderColor, borderColor),
|
||||
)
|
||||
|
||||
|
||||
val rippleColorStateList = ColorStateList(
|
||||
arrayOf(
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked)
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf(-android.R.attr.state_checked),
|
||||
),
|
||||
intArrayOf(rippleColorSelected, rippleColor)
|
||||
intArrayOf(rippleColorSelected, rippleColor),
|
||||
)
|
||||
|
||||
|
||||
button.backgroundTintList = bgColorStateList
|
||||
button.setTextColor(textColorStateList)
|
||||
button.strokeColor = strokeColorStateList
|
||||
@ -224,11 +227,11 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
val reactContext = context as? ReactContext ?: return
|
||||
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
||||
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
||||
|
||||
|
||||
val event = Arguments.createMap().apply {
|
||||
putInt("selectedIndex", selectedIndex)
|
||||
}
|
||||
|
||||
|
||||
eventDispatcher?.dispatchEvent(ChangeEvent(surfaceId, id, event))
|
||||
}
|
||||
|
||||
@ -250,11 +253,11 @@ class CustomSegmentedControl @JvmOverloads constructor(
|
||||
private inner class ChangeEvent(
|
||||
surfaceId: Int,
|
||||
viewId: Int,
|
||||
private val eventData: WritableMap
|
||||
private val eventData: WritableMap,
|
||||
) : Event<ChangeEvent>(surfaceId, viewId) {
|
||||
|
||||
|
||||
override fun getEventName(): String = "topChange"
|
||||
|
||||
|
||||
override fun getEventData(): WritableMap = eventData
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package io.bluewallet.bluewallet.components.segmentedcontrol
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.common.MapBuilder
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.SimpleViewManager
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
|
||||
@ReactModule(name = SegmentedControlManager.REACT_CLASS)
|
||||
class SegmentedControlManager : SimpleViewManager<SegmentedControl>() {
|
||||
|
||||
companion object {
|
||||
const val REACT_CLASS = "SegmentedControl"
|
||||
}
|
||||
|
||||
override fun getName(): String = REACT_CLASS
|
||||
|
||||
override fun createViewInstance(reactContext: ThemedReactContext): SegmentedControl =
|
||||
SegmentedControl(reactContext)
|
||||
|
||||
@ReactProp(name = "values")
|
||||
fun setValues(view: SegmentedControl, values: ReadableArray?) {
|
||||
view.values = values?.let { arr -> Array(arr.size()) { arr.getString(it) ?: "" } } ?: emptyArray()
|
||||
}
|
||||
|
||||
@ReactProp(name = "selectedIndex", defaultInt = 0)
|
||||
fun setSelectedIndex(view: SegmentedControl, selectedIndex: Int) {
|
||||
view.selectedIndex = selectedIndex
|
||||
}
|
||||
|
||||
@ReactProp(name = "enabled", defaultBoolean = true)
|
||||
fun setEnabled(view: SegmentedControl, enabled: Boolean) {
|
||||
view.setEnabledProp(enabled)
|
||||
}
|
||||
|
||||
@ReactProp(name = "momentary", defaultBoolean = false)
|
||||
fun setMomentary(view: SegmentedControl, momentary: Boolean) {
|
||||
view.setMomentaryProp(momentary)
|
||||
}
|
||||
|
||||
@ReactProp(name = "backgroundColor")
|
||||
fun setBackgroundColor(view: SegmentedControl, backgroundColor: String?) {
|
||||
view.setBackgroundColorProp(backgroundColor)
|
||||
}
|
||||
|
||||
@ReactProp(name = "tintColor")
|
||||
fun setTintColor(view: SegmentedControl, tintColor: String?) {
|
||||
view.setTintColorProp(tintColor)
|
||||
}
|
||||
|
||||
@ReactProp(name = "textColor")
|
||||
fun setTextColor(view: SegmentedControl, textColor: String?) {
|
||||
view.setTextColorProp(textColor)
|
||||
}
|
||||
|
||||
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any>? =
|
||||
MapBuilder.builder<String, Any>()
|
||||
.put(
|
||||
"topChange",
|
||||
MapBuilder.of(
|
||||
"phasedRegistrationNames",
|
||||
MapBuilder.of("bubbled", "onChange", "captured", "onChangeCapture"),
|
||||
),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
@ -5,13 +5,13 @@ import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
class CustomSegmentedControlPackage : ReactPackage {
|
||||
class SegmentedControlPackage : ReactPackage {
|
||||
|
||||
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||
return listOf(CustomSegmentedControlManager())
|
||||
return listOf(SegmentedControlManager())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE(SegmentedControlManager, RCTViewManager)
|
||||
|
||||
@end
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import React
|
||||
|
||||
@objc(SegmentedControlManager)
|
||||
final class SegmentedControlManager: RCTViewManager {
|
||||
|
||||
override class func requiresMainQueueSetup() -> Bool { true }
|
||||
|
||||
override func view() -> UIView! {
|
||||
return SegmentedControlView()
|
||||
}
|
||||
|
||||
@objc class func propConfig_values() -> [String]! { ["NSArray"] }
|
||||
@objc class func propConfig_selectedIndex() -> [String]! { ["NSInteger"] }
|
||||
@objc class func propConfig_enabled() -> [String]! { ["BOOL"] }
|
||||
@objc class func propConfig_momentary() -> [String]! { ["BOOL"] }
|
||||
@objc class func propConfig_tintColor() -> [String]! { ["UIColor"] }
|
||||
@objc class func propConfig_backgroundColor() -> [String]! { ["UIColor"] }
|
||||
@objc class func propConfig_textColor() -> [String]! { ["UIColor"] }
|
||||
@objc class func propConfig_onChange() -> [String]! { ["RCTBubblingEventBlock"] }
|
||||
}
|
||||
|
||||
@ -0,0 +1,98 @@
|
||||
import UIKit
|
||||
import React
|
||||
|
||||
@objc(SegmentedControlView)
|
||||
final class SegmentedControlView: UIView {
|
||||
|
||||
private let segmentedControl = UISegmentedControl()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setup()
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
segmentedControl.addTarget(self, action: #selector(handleValueChanged(_:)), for: .valueChanged)
|
||||
addSubview(segmentedControl)
|
||||
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
segmentedControl.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
segmentedControl.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
segmentedControl.topAnchor.constraint(equalTo: topAnchor),
|
||||
segmentedControl.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
// MARK: - Prop setters
|
||||
|
||||
@objc var values: NSArray = [] {
|
||||
didSet { rebuildSegments() }
|
||||
}
|
||||
|
||||
@objc var selectedIndex: Int = 0 {
|
||||
didSet {
|
||||
guard segmentedControl.numberOfSegments > 0 else { return }
|
||||
let clamped = min(max(selectedIndex, 0), segmentedControl.numberOfSegments - 1)
|
||||
if segmentedControl.selectedSegmentIndex != clamped {
|
||||
segmentedControl.selectedSegmentIndex = clamped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc var enabled: Bool = true {
|
||||
didSet { segmentedControl.isEnabled = enabled }
|
||||
}
|
||||
|
||||
@objc var momentary: Bool = false {
|
||||
didSet { segmentedControl.isMomentary = momentary }
|
||||
}
|
||||
|
||||
@objc var textColor: UIColor? {
|
||||
didSet { applyTextAttributes() }
|
||||
}
|
||||
|
||||
@objc var onChange: RCTBubblingEventBlock?
|
||||
|
||||
override var tintColor: UIColor! {
|
||||
didSet { segmentedControl.selectedSegmentTintColor = tintColor }
|
||||
}
|
||||
|
||||
override var backgroundColor: UIColor? {
|
||||
didSet { segmentedControl.backgroundColor = backgroundColor }
|
||||
}
|
||||
|
||||
// MARK: - Private helpers
|
||||
|
||||
private func rebuildSegments() {
|
||||
let titles = values as? [String] ?? []
|
||||
segmentedControl.removeAllSegments()
|
||||
for (i, title) in titles.enumerated() {
|
||||
segmentedControl.insertSegment(withTitle: title, at: i, animated: false)
|
||||
}
|
||||
guard !titles.isEmpty else { return }
|
||||
let clamped = min(max(selectedIndex, 0), titles.count - 1)
|
||||
segmentedControl.selectedSegmentIndex = clamped
|
||||
}
|
||||
|
||||
private func applyTextAttributes() {
|
||||
if let color = textColor {
|
||||
segmentedControl.setTitleTextAttributes([.foregroundColor: color], for: .normal)
|
||||
segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
|
||||
} else {
|
||||
segmentedControl.setTitleTextAttributes(nil, for: .normal)
|
||||
segmentedControl.setTitleTextAttributes(nil, for: .selected)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func handleValueChanged(_ sender: UISegmentedControl) {
|
||||
onChange?(["selectedIndex": sender.selectedSegmentIndex])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import type { ViewProps } from 'react-native';
|
||||
import type { BubblingEventHandler, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
||||
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
||||
|
||||
export interface NativeProps extends ViewProps {
|
||||
values: string[];
|
||||
selectedIndex: Int32;
|
||||
enabled: boolean;
|
||||
backgroundColor: string;
|
||||
tintColor: string;
|
||||
textColor: string;
|
||||
momentary: boolean;
|
||||
onChange?: BubblingEventHandler<Readonly<{ selectedIndex: Int32 }>>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<NativeProps>('CustomSegmentedControl');
|
||||
22
codegen/SegmentedControlNativeComponent.ts
Normal file
22
codegen/SegmentedControlNativeComponent.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type { HostComponent } from 'react-native';
|
||||
import type { ViewProps } from 'react-native';
|
||||
import type { BubblingEventHandler, Int32, WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
|
||||
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
||||
|
||||
type SegmentedControlChangeEvent = Readonly<{
|
||||
selectedIndex: Int32;
|
||||
target: Int32;
|
||||
}>;
|
||||
|
||||
export interface NativeProps extends ViewProps {
|
||||
values?: ReadonlyArray<string>;
|
||||
selectedIndex?: WithDefault<Int32, 0>;
|
||||
enabled?: WithDefault<boolean, true>;
|
||||
backgroundColor?: string | null;
|
||||
tintColor?: string | null;
|
||||
textColor?: string | null;
|
||||
momentary?: WithDefault<boolean, false>;
|
||||
onChange?: BubblingEventHandler<SegmentedControlChangeEvent> | null;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<NativeProps>('SegmentedControl') as HostComponent<NativeProps>;
|
||||
@ -2,12 +2,10 @@ import React, { forwardRef, ReactNode, useEffect, useRef, useState, useCallback,
|
||||
import {
|
||||
Animated,
|
||||
LayoutAnimation,
|
||||
Platform,
|
||||
PixelRatio,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
UIManager,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
StyleProp,
|
||||
@ -19,10 +17,6 @@ import { useSizeClass, SizeClass } from '../blue_modules/sizeClass';
|
||||
import { isDesktop } from '../blue_modules/environment';
|
||||
import debounce from '../blue_modules/debounce';
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
const scheduleInNextFrame = (callback: () => void): number => {
|
||||
return requestAnimationFrame(() => {
|
||||
// Use a second requestAnimationFrame to ensure we're not in the same frame
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { View, StyleSheet, NativeSyntheticEvent } from 'react-native';
|
||||
import NativeSegmentedControl from '../codegen/SegmentControlNativeComponent';
|
||||
import NativeSegmentedControl from '../codegen/SegmentedControlNativeComponent';
|
||||
|
||||
interface SegmentedControlProps {
|
||||
values: string[];
|
||||
selectedIndex: number;
|
||||
onChange: (index: number) => void;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
interface SegmentedControlEvent {
|
||||
selectedIndex: number;
|
||||
}
|
||||
|
||||
const SegmentedControl: React.FC<SegmentedControlProps> = ({ values, selectedIndex, onChange }) => {
|
||||
const handleChange = useMemo(
|
||||
() => (event: NativeSyntheticEvent<SegmentedControlEvent>) => {
|
||||
const SegmentedControl: React.FC<SegmentedControlProps> = ({ values, selectedIndex, onChange, testID }) => {
|
||||
const handleChange = useCallback(
|
||||
(event: NativeSyntheticEvent<SegmentedControlEvent>) => {
|
||||
if (event?.nativeEvent?.selectedIndex !== undefined) {
|
||||
onChange(event.nativeEvent.selectedIndex);
|
||||
}
|
||||
@ -38,6 +39,7 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({ values, selectedInd
|
||||
momentary={false}
|
||||
style={styles.segmentedControl}
|
||||
onChange={handleChange}
|
||||
testID={testID}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
6
index.js
6
index.js
@ -4,7 +4,7 @@ import 'react-native-get-random-values';
|
||||
import './shim.js';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { AppRegistry, LogBox } from 'react-native';
|
||||
import { AppRegistry, LogBox, Platform, UIManager } from 'react-native';
|
||||
|
||||
import App from './App';
|
||||
import { restoreSavedPreferredFiatCurrencyAndExchangeFromStorage } from './blue_modules/currency';
|
||||
@ -14,6 +14,10 @@ if (!Error.captureStackTrace) {
|
||||
Error.captureStackTrace = () => {};
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
LogBox.ignoreLogs([
|
||||
'Require cycle:',
|
||||
'Battery state `unknown` and monitoring disabled, this is normal for simulators and tvOS.',
|
||||
|
||||
@ -148,8 +148,10 @@
|
||||
B4AB225E2B02AD12001F4328 /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */; };
|
||||
B4B1A4622BFA73110072E3BB /* WidgetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */; };
|
||||
B4B1A4642BFA73110072E3BB /* WidgetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */; };
|
||||
B4B3EC222D69FF6C00327F3D /* CustomSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3EC202D69FF6C00327F3D /* CustomSegmentedControl.swift */; };
|
||||
B4B3EC222D69FF6C00327F3D /* SegmentedControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3EC202D69FF6C00327F3D /* SegmentedControlView.swift */; };
|
||||
B4B3EC252D69FF8700327F3D /* EventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3EC232D69FF8700327F3D /* EventEmitter.swift */; };
|
||||
B4B3EF102D6AFF6C003270A0 /* SegmentedControlManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3EF002D6AFF6C003270A0 /* SegmentedControlManager.swift */; };
|
||||
B4B3EF202D6CFF6C003270A0 /* SegmentedControlBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B4B3EF1F2D6CFF6C003270A0 /* SegmentedControlBridge.m */; };
|
||||
B4D0B2622C1DEA11006B6B1B /* ReceivePageInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2612C1DEA11006B6B1B /* ReceivePageInterfaceController.swift */; };
|
||||
B4D0B2642C1DEA99006B6B1B /* ReceiveType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2632C1DEA99006B6B1B /* ReceiveType.swift */; };
|
||||
B4D0B2662C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2652C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift */; };
|
||||
@ -159,7 +161,6 @@
|
||||
B4D59C1E2D8BAFE300B7025B /* BugsnagNetworkRequestPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = B4D59C1D2D8BAFE300B7025B /* BugsnagNetworkRequestPlugin */; };
|
||||
B4D59C212D8BB42100B7025B /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D59C202D8BB41F00B7025B /* File.swift */; };
|
||||
B4D59C272D8C5D6F00B7025B /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D59C262D8C5D6E00B7025B /* main.swift */; };
|
||||
B4D899942DCAE67700B959AA /* CustomSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B4D899932DCAE67700B959AA /* CustomSegmentedControl.m */; };
|
||||
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
|
||||
B4EFF73B2C3F6C5E0095D655 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EFF73A2C3F6C5E0095D655 /* MockData.swift */; };
|
||||
B4F0A4A22FA1BC0000AAAA01 /* WidgetHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4F0A4A12FA1BC0000AAAA00 /* WidgetHelper.mm */; };
|
||||
@ -366,8 +367,10 @@
|
||||
B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLParserDelegate.swift; sourceTree = "<group>"; };
|
||||
B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetHelper.swift; sourceTree = "<group>"; };
|
||||
B4B31A352C77BBA000663334 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Interface.strings; sourceTree = "<group>"; };
|
||||
B4B3EC202D69FF6C00327F3D /* CustomSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomSegmentedControl.swift; path = SegmentedControl/CustomSegmentedControl.swift; sourceTree = "<group>"; };
|
||||
B4B3EC202D69FF6C00327F3D /* SegmentedControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SegmentedControlView.swift; path = ../blue_modules/Views/SegmentedControl/ios/SegmentedControlView.swift; sourceTree = SOURCE_ROOT; };
|
||||
B4B3EC232D69FF8700327F3D /* EventEmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventEmitter.swift; sourceTree = "<group>"; };
|
||||
B4B3EF002D6AFF6C003270A0 /* SegmentedControlManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SegmentedControlManager.swift; path = ../blue_modules/Views/SegmentedControl/ios/SegmentedControlManager.swift; sourceTree = SOURCE_ROOT; };
|
||||
B4B3EF1F2D6CFF6C003270A0 /* SegmentedControlBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SegmentedControlBridge.m; path = ../blue_modules/Views/SegmentedControl/ios/SegmentedControlBridge.m; sourceTree = SOURCE_ROOT; };
|
||||
B4D0B2612C1DEA11006B6B1B /* ReceivePageInterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceivePageInterfaceController.swift; sourceTree = "<group>"; };
|
||||
B4D0B2632C1DEA99006B6B1B /* ReceiveType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveType.swift; sourceTree = "<group>"; };
|
||||
B4D0B2652C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveInterfaceMode.swift; sourceTree = "<group>"; };
|
||||
@ -375,7 +378,6 @@
|
||||
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCSlider.a; sourceTree = "<group>"; };
|
||||
B4D59C202D8BB41F00B7025B /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||
B4D59C262D8C5D6E00B7025B /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
B4D899932DCAE67700B959AA /* CustomSegmentedControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomSegmentedControl.m; sourceTree = "<group>"; };
|
||||
B4EFF73A2C3F6C5E0095D655 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = "<group>"; };
|
||||
B4F0A4A12FA1BC0000AAAA00 /* WidgetHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WidgetHelper.mm; sourceTree = "<group>"; };
|
||||
B4F0A4A32FA1BC0000AAAA02 /* EventEmitter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = EventEmitter.mm; sourceTree = "<group>"; };
|
||||
@ -746,12 +748,13 @@
|
||||
B45010A12C1504E900619044 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B4D899932DCAE67700B959AA /* CustomSegmentedControl.m */,
|
||||
B4AA75232DAA339E00CF5CBE /* MenuElementsEmitter.m */,
|
||||
B4B3EC232D69FF8700327F3D /* EventEmitter.swift */,
|
||||
B4F0A4A32FA1BC0000AAAA02 /* EventEmitter.mm */,
|
||||
B409AB052D71E07500BA06F8 /* MenuElementsEmitter.swift */,
|
||||
B4B3EC202D69FF6C00327F3D /* CustomSegmentedControl.swift */,
|
||||
B4B3EC202D69FF6C00327F3D /* SegmentedControlView.swift */,
|
||||
B4B3EF002D6AFF6C003270A0 /* SegmentedControlManager.swift */,
|
||||
B4B3EF1F2D6CFF6C003270A0 /* SegmentedControlBridge.m */,
|
||||
B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */,
|
||||
B4F0A4A12FA1BC0000AAAA00 /* WidgetHelper.mm */,
|
||||
);
|
||||
@ -1162,10 +1165,11 @@
|
||||
B48630EE2CCEEEE900A8425C /* PriceIntent.swift in Sources */,
|
||||
B44034072BCC38A000162242 /* FiatUnit.swift in Sources */,
|
||||
B44034002BCC37F800162242 /* Bundle+decode.swift in Sources */,
|
||||
B4D899942DCAE67700B959AA /* CustomSegmentedControl.m in Sources */,
|
||||
B44033E22BCC36CB00162242 /* Placeholders.swift in Sources */,
|
||||
B4793DBB2CEDACBD00C92C2E /* Chain.swift in Sources */,
|
||||
B4B3EC222D69FF6C00327F3D /* CustomSegmentedControl.swift in Sources */,
|
||||
B4B3EC222D69FF6C00327F3D /* SegmentedControlView.swift in Sources */,
|
||||
B4B3EF102D6AFF6C003270A0 /* SegmentedControlManager.swift in Sources */,
|
||||
B4B3EF202D6CFF6C003270A0 /* SegmentedControlBridge.m in Sources */,
|
||||
B4B1A4622BFA73110072E3BB /* WidgetHelper.swift in Sources */,
|
||||
B4F0A4A22FA1BC0000AAAA01 /* WidgetHelper.mm in Sources */,
|
||||
B48630E12CCEE7C800A8425C /* PriceWidgetEntryView.swift in Sources */,
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
//
|
||||
// RCT.h
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 4/22/25.
|
||||
// Copyright © 2025 BlueWallet. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE(CustomSegmentedControlManager, RCTViewManager)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(values, NSArray)
|
||||
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
||||
|
||||
@end
|
||||
@ -1,47 +0,0 @@
|
||||
import UIKit
|
||||
import React
|
||||
|
||||
@objc(CustomSegmentedControl)
|
||||
class CustomSegmentedControl: UISegmentedControl {
|
||||
@objc var onChange: RCTBubblingEventBlock?
|
||||
|
||||
@objc var values: [String] = [] {
|
||||
didSet {
|
||||
removeAllSegments()
|
||||
for (index, title) in values.enumerated() {
|
||||
insertSegment(withTitle: title, at: index, animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc var selectedIndex: NSNumber = 0 {
|
||||
didSet {
|
||||
self.selectedSegmentIndex = selectedIndex.intValue
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
addTarget(self, action: #selector(onChange(_:)), for: .valueChanged)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
addTarget(self, action: #selector(onChange(_:)), for: .valueChanged)
|
||||
}
|
||||
|
||||
@objc func onChange(_ sender: UISegmentedControl) {
|
||||
onChange?(["selectedIndex": sender.selectedSegmentIndex])
|
||||
}
|
||||
}
|
||||
|
||||
@objc(CustomSegmentedControlManager)
|
||||
class CustomSegmentedControlManager: RCTViewManager {
|
||||
override func view() -> UIView! {
|
||||
return CustomSegmentedControl(frame: .zero)
|
||||
}
|
||||
|
||||
override class func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1991,7 +1991,7 @@ PODS:
|
||||
- React-perflogger (= 0.84.1)
|
||||
- React-utils (= 0.84.1)
|
||||
- ReactNativeDependencies
|
||||
- ReactNativeCameraKit (17.0.3):
|
||||
- ReactNativeCameraKit (17.0.4):
|
||||
- hermes-engine
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
@ -2323,7 +2323,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- ReactNativeDependencies
|
||||
- Yoga
|
||||
- RNSVG (15.15.3):
|
||||
- RNSVG (15.15.4):
|
||||
- hermes-engine
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
@ -2344,9 +2344,9 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- ReactNativeDependencies
|
||||
- RNSVG/common (= 15.15.3)
|
||||
- RNSVG/common (= 15.15.4)
|
||||
- Yoga
|
||||
- RNSVG/common (15.15.3):
|
||||
- RNSVG/common (15.15.4):
|
||||
- hermes-engine
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
@ -2801,7 +2801,7 @@ SPEC CHECKSUMS:
|
||||
BVLinearGradient: cb006ba232a1f3e4f341bb62c42d1098c284da70
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da
|
||||
hermes-engine: 5b6b255f9e4ba0f6f699433393c9ca57c402fc96
|
||||
hermes-engine: 26a52ac276936854ac2797880e2c181aa1fb0025
|
||||
lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3
|
||||
lottie-react-native: 6a080b2f109ef611c75c503a33ebb8ea75db0c91
|
||||
RCTDeprecation: af44b104091a34482596cd9bd7e8d90c4e9b4bd7
|
||||
@ -2812,7 +2812,7 @@ SPEC CHECKSUMS:
|
||||
React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b
|
||||
React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b
|
||||
React-Core: 7840d3a80b43a95c5e80ef75146bd70925ebab0f
|
||||
React-Core-prebuilt: fe445abdc8b577160a3ecf632edd6545f00f10cf
|
||||
React-Core-prebuilt: 96b4b7f11b336f3b9d498eca3460c82ee260b31e
|
||||
React-CoreModules: 2eb010400b63b89e53a324ffb3c112e4c7c3ce42
|
||||
React-cxxreact: a558e92199d26f145afa9e62c4233cf8e7950efe
|
||||
React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc
|
||||
@ -2889,10 +2889,10 @@ SPEC CHECKSUMS:
|
||||
React-utils: 8d888b379f0808bfabaea03d85f9e8dd9b8548da
|
||||
React-webperformancenativemodule: c10016db7f1bb1153060d4aa9f7dbde2c88c845d
|
||||
ReactAppDependencyProvider: e96e93b493d8d86eeaee3e590ba0be53f6abe46f
|
||||
ReactCodegen: c8cd59a80d11b8c2f8e70fab3cdc62673ae56f1b
|
||||
ReactCodegen: 333ec6399ca7299a87623ad07db17874502f9d64
|
||||
ReactCommon: 07572bf9e687c8a52fbe4a3641e9e3a1a477c78e
|
||||
ReactNativeCameraKit: 2f24ad111e0f525a6529dd0eff5bf5d4454a24ba
|
||||
ReactNativeDependencies: 9f044a84d7bddd9822da88447b4a37e7cc085862
|
||||
ReactNativeCameraKit: f4911c327342c1f34aae1346f9d5f5ae3030b0f6
|
||||
ReactNativeDependencies: d91ea5381a1df88b4bc29bfb8a525ececa743c3d
|
||||
RealmJS: 1c37c6bdfe060f4caa0f9175aa0eedb962622ee1
|
||||
RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4
|
||||
RNCClipboard: 88d7eeb555d1183915f0885bdbc5c97eb6f7f3ba
|
||||
@ -2909,7 +2909,7 @@ SPEC CHECKSUMS:
|
||||
RNReanimated: 66fa99647173f254f731e6aa59a0a2cc8c838ac1
|
||||
RNScreens: 6cb648bdad8fe9bee9259fe144df95b6d1d5b707
|
||||
RNShare: aad7f2a80ae80be5e2bf57583be28b1a18f95de1
|
||||
RNSVG: 507bf2685de6b3d49449efd4aae7e7471bb9c433
|
||||
RNSVG: c69f7709226108f5eb89b5aa8833c17a36345468
|
||||
RNWatch: 28fe1f5e0c6410d45fd20925f4796fce05522e3f
|
||||
RNWorklets: 30f9a363d681c776a5f9d74f6d21a6d1bb7bfe80
|
||||
Yoga: c0b3f2c7e8d3e327e450223a2414ca3fa296b9a2
|
||||
|
||||
@ -16,7 +16,7 @@ import CopyTextToClipboard from '../../components/CopyTextToClipboard';
|
||||
import HandOffComponent from '../../components/HandOffComponent';
|
||||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||
import QRCodeComponent from '../../components/QRCodeComponent';
|
||||
import SegmentedControl from '../../components/SegmentControl';
|
||||
import SegmentedControl from '../../components/SegmentedControl';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import TipBox from '../../components/TipBox';
|
||||
import { TransactionPendingIconBig } from '../../components/TransactionPendingIconBig';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import dayjs from 'dayjs';
|
||||
import calendar from 'dayjs/plugin/calendar';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { Keyboard, LayoutAnimation, NativeSyntheticEvent, Platform, StyleSheet, UIManager, View } from 'react-native';
|
||||
import { Keyboard, LayoutAnimation, NativeSyntheticEvent, StyleSheet, View } from 'react-native';
|
||||
|
||||
import {
|
||||
CurrencyRate,
|
||||
@ -26,10 +26,6 @@ import { FiatUnit, FiatUnitSource, FiatUnitType, getFiatRate } from '../../model
|
||||
|
||||
dayjs.extend(calendar);
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
const MAX_DISPLAY_ITEMS = 50;
|
||||
|
||||
const Currency: React.FC = () => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import { ActivityIndicator, FlatList, LayoutAnimation, Platform, StyleSheet, UIManager, View } from 'react-native';
|
||||
import { ActivityIndicator, FlatList, LayoutAnimation, Platform, StyleSheet, View } from 'react-native';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { BlueButtonLink, BlueFormLabel, BlueText } from '../../BlueComponents';
|
||||
import { HDSegwitBech32Wallet } from '../../class/wallets/hd-segwit-bech32-wallet';
|
||||
@ -31,10 +31,6 @@ type WalletEntry = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
const ImportWalletDiscovery: React.FC = () => {
|
||||
const navigation = useExtendedNavigation<NavigationProp>();
|
||||
const { colors } = useTheme();
|
||||
|
||||
@ -1,17 +1,5 @@
|
||||
import React, { useEffect, useLayoutEffect, useReducer, useCallback, useMemo, useRef, useState, lazy, Suspense } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
Alert,
|
||||
Animated,
|
||||
ActivityIndicator,
|
||||
UIManager,
|
||||
Platform,
|
||||
Keyboard,
|
||||
Text,
|
||||
Pressable,
|
||||
} from 'react-native';
|
||||
import { StyleSheet, TouchableOpacity, Image, Alert, Animated, ActivityIndicator, Keyboard, Text, Pressable } from 'react-native';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useLocale, usePreventRemove } from '@react-navigation/native';
|
||||
@ -154,10 +142,6 @@ const reducer = (state: State, action: Action): State => {
|
||||
}
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
const ManageWallets: React.FC = () => {
|
||||
const { colors, closeImage } = useTheme();
|
||||
const { wallets: persistedWallets, setWalletsWithNewOrder, txMetadata } = useStorage();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useReducer, useMemo } from 'react';
|
||||
import { useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
|
||||
import { ActivityIndicator, FlatList, StyleSheet, View, Platform, UIManager } from 'react-native';
|
||||
import { ActivityIndicator, FlatList, StyleSheet, View } from 'react-native';
|
||||
import { WatchOnlyWallet } from '../../class/wallets/watch-only-wallet';
|
||||
import { AddressItem } from '../../components/addresses/AddressItem';
|
||||
import { useTheme } from '../../components/themes';
|
||||
@ -8,7 +8,7 @@ import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import SegmentedControl from '../../components/SegmentControl';
|
||||
import SegmentedControl from '../../components/SegmentedControl';
|
||||
import loc from '../../loc';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
@ -21,10 +21,6 @@ export const TABS = {
|
||||
|
||||
type TabKey = keyof typeof TABS;
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
interface Address {
|
||||
key: string;
|
||||
index: number;
|
||||
|
||||
42
tests/unit/segmented-control.test.tsx
Normal file
42
tests/unit/segmented-control.test.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import SegmentedControl from '../../components/SegmentedControl';
|
||||
|
||||
jest.mock('../../codegen/SegmentedControlNativeComponent', () => {
|
||||
const { View } = require('react-native');
|
||||
const MockSegmentedControl = ({ values, selectedIndex, onChange, testID }: any) =>
|
||||
React.createElement(View, { testID: testID ?? 'segmented-control' });
|
||||
return {
|
||||
__esModule: true,
|
||||
default: MockSegmentedControl,
|
||||
};
|
||||
});
|
||||
|
||||
describe('SegmentedControl', () => {
|
||||
const VALUES = ['One', 'Two', 'Three'];
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const { getByTestId } = render(<SegmentedControl values={VALUES} selectedIndex={0} onChange={jest.fn()} testID="sc" />);
|
||||
expect(getByTestId('sc')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns null when values array is empty', () => {
|
||||
const { toJSON } = render(<SegmentedControl values={[]} selectedIndex={0} onChange={jest.fn()} />);
|
||||
expect(toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
it('passes values and selectedIndex to native component', () => {
|
||||
const { getByTestId } = render(<SegmentedControl values={VALUES} selectedIndex={1} onChange={jest.fn()} testID="sc2" />);
|
||||
expect(getByTestId('sc2')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('calls onChange with the correct index when selection changes', () => {
|
||||
const onChangeMock = jest.fn();
|
||||
const { UNSAFE_getAllByType } = render(<SegmentedControl values={VALUES} selectedIndex={0} onChange={onChangeMock} />);
|
||||
const MockSegmentedControl = require('../../codegen/SegmentedControlNativeComponent').default;
|
||||
const [instance] = UNSAFE_getAllByType(MockSegmentedControl);
|
||||
// Simulate native onChange event
|
||||
instance.props.onChange?.({ nativeEvent: { selectedIndex: 2 } });
|
||||
expect(onChangeMock).toHaveBeenCalledWith(2);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user