mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 13:26:33 +01:00
Merge pull request #6679 from BlueWallet/seg
ADD: Use native segment controller.
This commit is contained in:
commit
1a81449c40
@ -109,6 +109,7 @@ dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
}
|
||||
apply plugin: 'com.google.gms.google-services' // Google Services plugin
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomSegmentControlPackage implements ReactPackage {
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
CustomSegmentedControlManager.registerIfNecessary();
|
||||
List<ViewManager> viewManagers = new ArrayList<>();
|
||||
viewManagers.add(new CustomSegmentedControlManager());
|
||||
return viewManagers;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.LinearLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.react.uimanager.SimpleViewManager;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
public class CustomSegmentedControlManager extends SimpleViewManager<CustomSegmentedControlManager.CustomSegmentedControlView> {
|
||||
public static final String REACT_CLASS = "CustomSegmentedControl";
|
||||
private static boolean isRegistered = false;
|
||||
|
||||
public static class CustomSegmentedControlView extends LinearLayout {
|
||||
private TabLayout tabLayout;
|
||||
private RCTEventEmitter eventEmitter;
|
||||
private int viewId;
|
||||
|
||||
public CustomSegmentedControlView(Context context) {
|
||||
super(context);
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
tabLayout = new TabLayout(context);
|
||||
addView(tabLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
WritableMap event = Arguments.createMap();
|
||||
event.putInt("selectedIndex", tab.getPosition());
|
||||
if (eventEmitter != null) {
|
||||
eventEmitter.receiveEvent(viewId, "topChange", event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {}
|
||||
});
|
||||
}
|
||||
|
||||
public void setValues(ReadableArray values) {
|
||||
tabLayout.removeAllTabs();
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
tabLayout.addTab(tabLayout.newTab().setText(values.getString(i)));
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedIndex(int selectedIndex) {
|
||||
if (selectedIndex >= 0 && selectedIndex < tabLayout.getTabCount()) {
|
||||
TabLayout.Tab tab = tabLayout.getTabAt(selectedIndex);
|
||||
if (tab != null) {
|
||||
tab.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setEventEmitter(RCTEventEmitter eventEmitter, int viewId) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.viewId = viewId;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected CustomSegmentedControlView createViewInstance(@NonNull ThemedReactContext reactContext) {
|
||||
CustomSegmentedControlView view = new CustomSegmentedControlView(reactContext);
|
||||
view.setEventEmitter(reactContext.getJSModule(RCTEventEmitter.class), view.getId());
|
||||
return view;
|
||||
}
|
||||
|
||||
@ReactProp(name = "values")
|
||||
public void setValues(CustomSegmentedControlView view, ReadableArray values) {
|
||||
view.setValues(values);
|
||||
}
|
||||
|
||||
@ReactProp(name = "selectedIndex")
|
||||
public void setSelectedIndex(CustomSegmentedControlView view, int selectedIndex) {
|
||||
view.setSelectedIndex(selectedIndex);
|
||||
}
|
||||
|
||||
public static void registerIfNecessary() {
|
||||
if (!isRegistered) {
|
||||
isRegistered = true;
|
||||
// Registration logic if necessary
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,8 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
packages.add(new CustomSegmentControlPackage());
|
||||
CustomSegmentedControlManager.registerIfNecessary();
|
||||
return packages;
|
||||
}
|
||||
|
||||
@ -40,15 +41,15 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@ -62,19 +63,19 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
|
||||
sharedI18nUtilInstance.allowRTL(getApplicationContext(), true);
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE);
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE);
|
||||
|
||||
// Retrieve the "donottrack" value. Default to "0" if not found.
|
||||
String isDoNotTrackEnabled = sharedPref.getString("donottrack", "0");
|
||||
// Retrieve the "donottrack" value. Default to "0" if not found.
|
||||
String isDoNotTrackEnabled = sharedPref.getString("donottrack", "0");
|
||||
|
||||
// Check if do not track is not enabled and initialize Bugsnag if so
|
||||
if (!isDoNotTrackEnabled.equals("1")) {
|
||||
// Initialize Bugsnag or your error tracking here
|
||||
Bugsnag.start(this);
|
||||
}
|
||||
// Check if do not track is not enabled and initialize Bugsnag if so
|
||||
if (!isDoNotTrackEnabled.equals("1")) {
|
||||
// Initialize Bugsnag or your error tracking here
|
||||
Bugsnag.start(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
components/SegmentControl.tsx
Normal file
44
components/SegmentControl.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { requireNativeComponent, View, StyleSheet, NativeSyntheticEvent } from 'react-native';
|
||||
|
||||
interface SegmentedControlProps {
|
||||
values: string[];
|
||||
selectedIndex: number;
|
||||
onChange: (index: number) => void;
|
||||
}
|
||||
|
||||
interface SegmentedControlEvent {
|
||||
selectedIndex: number;
|
||||
}
|
||||
|
||||
interface NativeSegmentedControlProps {
|
||||
values: string[];
|
||||
selectedIndex: number;
|
||||
onChangeEvent: (event: NativeSyntheticEvent<SegmentedControlEvent>) => void;
|
||||
style?: object;
|
||||
}
|
||||
|
||||
const NativeSegmentedControl = requireNativeComponent<NativeSegmentedControlProps>('CustomSegmentedControl');
|
||||
|
||||
const SegmentedControl: React.FC<SegmentedControlProps> = ({ values, selectedIndex, onChange }) => {
|
||||
const handleChange = (event: NativeSyntheticEvent<SegmentedControlEvent>) => {
|
||||
onChange(event.nativeEvent.selectedIndex);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<NativeSegmentedControl values={values} selectedIndex={selectedIndex} style={styles.segmentedControl} onChangeEvent={handleChange} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
},
|
||||
segmentedControl: {
|
||||
height: 40,
|
||||
},
|
||||
});
|
||||
|
||||
export default SegmentedControl;
|
@ -1,109 +0,0 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View, Pressable, LayoutAnimation, Platform, UIManager, ViewStyle, TextStyle } from 'react-native';
|
||||
import loc from '../../loc';
|
||||
import { useTheme } from '../themes';
|
||||
|
||||
export const TABS = {
|
||||
EXTERNAL: 'receive',
|
||||
INTERNAL: 'change',
|
||||
} as const;
|
||||
|
||||
type TabKey = keyof typeof TABS;
|
||||
type TABS_VALUES = (typeof TABS)[keyof typeof TABS];
|
||||
|
||||
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
interface AddressTypeTabsProps {
|
||||
currentTab: TABS_VALUES;
|
||||
setCurrentTab: (tab: TABS_VALUES) => void;
|
||||
customTabText?: { [key in TabKey]?: string };
|
||||
}
|
||||
|
||||
const AddressTypeTabs: React.FC<AddressTypeTabsProps> = ({ currentTab, setCurrentTab, customTabText }) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
activeTab: {
|
||||
backgroundColor: colors.modal,
|
||||
} as ViewStyle,
|
||||
activeText: {
|
||||
fontWeight: 'bold',
|
||||
color: colors.foregroundColor,
|
||||
} as TextStyle,
|
||||
inactiveTab: {
|
||||
fontWeight: 'normal',
|
||||
color: colors.foregroundColor,
|
||||
} as TextStyle,
|
||||
backTabs: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
} as ViewStyle,
|
||||
});
|
||||
|
||||
const tabs = Object.entries(TABS).map(([key, value]) => {
|
||||
return {
|
||||
key: key as TabKey,
|
||||
value,
|
||||
name: customTabText?.[key as TabKey] || loc.addresses[`type_${value}`],
|
||||
};
|
||||
});
|
||||
|
||||
const changeToTab = (tabKey: TabKey) => {
|
||||
if (tabKey in TABS) {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCurrentTab(TABS[tabKey]);
|
||||
}
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const tabsButtons = tabs.map(tab => {
|
||||
const isActive = tab.value === currentTab;
|
||||
|
||||
const tabStyle = isActive ? stylesHook.activeTab : undefined;
|
||||
const textStyle = isActive ? stylesHook.activeText : stylesHook.inactiveTab;
|
||||
|
||||
return (
|
||||
<Pressable key={tab.key} onPress={() => changeToTab(tab.key)} style={[styles.tab, tabStyle]}>
|
||||
<Text style={textStyle}>{tab.name}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[stylesHook.backTabs, styles.backTabs]}>
|
||||
<View style={styles.tabs}>{tabsButtons}</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return render();
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
} as ViewStyle,
|
||||
backTabs: {
|
||||
padding: 4,
|
||||
marginVertical: 8,
|
||||
borderRadius: 8,
|
||||
} as ViewStyle,
|
||||
tabs: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
} as ViewStyle,
|
||||
tab: {
|
||||
borderRadius: 6,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: 'center',
|
||||
} as ViewStyle,
|
||||
});
|
||||
|
||||
export { AddressTypeTabs };
|
@ -4,3 +4,4 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "React/RCTViewManager.h"
|
||||
|
@ -141,8 +141,10 @@
|
||||
B450109D2C0FCD9F00619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B450109E2C0FCDA000619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B450109F2C0FCDA500619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B45010A62C1507DE00619044 /* CustomSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B45010A52C1507DE00619044 /* CustomSegmentedControlManager.m */; };
|
||||
B4549F362B82B10D002E3153 /* ci_post_clone.sh in Resources */ = {isa = PBXBuildFile; fileRef = B4549F352B82B10D002E3153 /* ci_post_clone.sh */; };
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B461B851299599F800E431AA /* AppDelegate.mm */; };
|
||||
B47462D02C1538D800100825 /* CustomSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B45010A52C1507DE00619044 /* CustomSegmentedControlManager.m */; };
|
||||
B47B21EC2B2128B8001F6690 /* BlueWalletUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B47B21EB2B2128B8001F6690 /* BlueWalletUITests.swift */; };
|
||||
B49038D92B8FBAD300A8164A /* BlueWalletUITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49038D82B8FBAD300A8164A /* BlueWalletUITest.swift */; };
|
||||
B4A29A2C2B55C990002A67DF /* EventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D32C5C52596CE3A008C077C /* EventEmitter.m */; };
|
||||
@ -444,6 +446,8 @@
|
||||
B44033FF2BCC37F800162242 /* Bundle+decode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+decode.swift"; sourceTree = "<group>"; };
|
||||
B440340E2BCC40A400162242 /* fiatUnits.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = fiatUnits.json; path = ../../../models/fiatUnits.json; sourceTree = "<group>"; };
|
||||
B450109B2C0FCD8A00619044 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||
B45010A52C1507DE00619044 /* CustomSegmentedControlManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomSegmentedControlManager.m; sourceTree = "<group>"; };
|
||||
B45010A92C15080500619044 /* CustomSegmentedControlManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomSegmentedControlManager.h; sourceTree = "<group>"; };
|
||||
B4549F352B82B10D002E3153 /* ci_post_clone.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = "<group>"; };
|
||||
B461B850299599F800E431AA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BlueWallet/AppDelegate.h; sourceTree = "<group>"; };
|
||||
B461B851299599F800E431AA /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = BlueWallet/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
@ -715,7 +719,7 @@
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */,
|
||||
B45010A12C1504E900619044 /* Components */,
|
||||
B44033C82BCC34AC00162242 /* Shared */,
|
||||
B41C2E552BB3DCB8000FE097 /* PrivacyInfo.xcprivacy */,
|
||||
B4549F2E2B80FEA1002E3153 /* ci_scripts */,
|
||||
@ -862,6 +866,24 @@
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B45010A12C1504E900619044 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B4B1A4612BFA73110072E3BB /* WidgetHelper.swift */,
|
||||
B45010A82C1507F000619044 /* SegmentedControl */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B45010A82C1507F000619044 /* SegmentedControl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B45010A52C1507DE00619044 /* CustomSegmentedControlManager.m */,
|
||||
B45010A92C15080500619044 /* CustomSegmentedControlManager.h */,
|
||||
);
|
||||
path = SegmentedControl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B4549F2E2B80FEA1002E3153 /* ci_scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1521,6 +1543,7 @@
|
||||
6D32C5C62596CE3A008C077C /* EventEmitter.m in Sources */,
|
||||
B44033FE2BCC37D700162242 /* MarketAPI.swift in Sources */,
|
||||
B450109C2C0FCD8A00619044 /* Utilities.swift in Sources */,
|
||||
B45010A62C1507DE00619044 /* CustomSegmentedControlManager.m in Sources */,
|
||||
B44033CE2BCC352900162242 /* UserDefaultsGroup.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */,
|
||||
@ -1638,6 +1661,7 @@
|
||||
B44033D72BCC369400162242 /* UserDefaultsExtension.swift in Sources */,
|
||||
B44033F72BCC377F00162242 /* WidgetData.swift in Sources */,
|
||||
B44033E12BCC36CA00162242 /* Placeholders.swift in Sources */,
|
||||
B47462D02C1538D800100825 /* CustomSegmentedControlManager.m in Sources */,
|
||||
B44033E72BCC36FF00162242 /* WalletData.swift in Sources */,
|
||||
B44033E02BCC36C300162242 /* LatestTransaction.swift in Sources */,
|
||||
B44033D92BCC369900162242 /* Colors.swift in Sources */,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#import <React/RCTRootView.h>
|
||||
#import <Bugsnag/Bugsnag.h>
|
||||
#import "BlueWallet-Swift.h"
|
||||
#import "CustomSegmentedControlManager.h"
|
||||
|
||||
@interface AppDelegate() <UNUserNotificationCenterDelegate>
|
||||
|
||||
@ -21,6 +22,7 @@
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[CustomSegmentedControlManager registerIfNecessary];
|
||||
[self clearFilesIfNeeded];
|
||||
self.userDefaultsGroup = [[NSUserDefaults alloc] initWithSuiteName:@"group.io.bluewallet.bluewallet"];
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
//
|
||||
// SegmentedControlManager.h
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 6/8/24.
|
||||
// Copyright © 2024 BlueWallet. All rights reserved.
|
||||
//
|
||||
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface CustomSegmentedControlManager : RCTViewManager
|
||||
|
||||
+ (void)registerIfNecessary;
|
||||
|
||||
@end
|
@ -0,0 +1,62 @@
|
||||
#import "CustomSegmentedControlManager.h"
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
@interface CustomSegmentedControl : UISegmentedControl
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onChangeEvent;
|
||||
- (void)setValues:(NSArray<NSString *> *)values;
|
||||
- (void)setSelectedIndex:(NSNumber *)selectedIndex;
|
||||
@end
|
||||
|
||||
@implementation CustomSegmentedControl
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValues:(NSArray<NSString *> *)values {
|
||||
[self removeAllSegments];
|
||||
for (NSUInteger i = 0; i < values.count; i++) {
|
||||
[self insertSegmentWithTitle:values[i] atIndex:i animated:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSelectedIndex:(NSNumber *)selectedIndex {
|
||||
self.selectedSegmentIndex = selectedIndex.integerValue;
|
||||
}
|
||||
|
||||
- (void)onChange:(UISegmentedControl *)sender {
|
||||
if (self.onChangeEvent) {
|
||||
self.onChangeEvent(@{@"selectedIndex": @(self.selectedSegmentIndex)});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CustomSegmentedControlManager
|
||||
|
||||
static BOOL isRegistered = NO;
|
||||
|
||||
RCT_EXPORT_MODULE(CustomSegmentedControl)
|
||||
|
||||
- (UIView *)view {
|
||||
return [CustomSegmentedControl new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(values, NSArray)
|
||||
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onChangeEvent, RCTDirectEventBlock)
|
||||
|
||||
+ (void)registerIfNecessary {
|
||||
if (!isRegistered) {
|
||||
isRegistered = YES;
|
||||
// Registration logic if necessary
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -3,13 +3,21 @@ import { useFocusEffect, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { ActivityIndicator, FlatList, StyleSheet, View } from 'react-native';
|
||||
import { WatchOnlyWallet } from '../../class';
|
||||
import { AddressItem } from '../../components/addresses/AddressItem';
|
||||
import { AddressTypeTabs, TABS } from '../../components/addresses/AddressTypeTabs';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import usePrivacy from '../../hooks/usePrivacy';
|
||||
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 loc from '../../loc';
|
||||
|
||||
export const TABS = {
|
||||
EXTERNAL: 'receive',
|
||||
INTERNAL: 'change',
|
||||
} as const;
|
||||
|
||||
type TabKey = keyof typeof TABS;
|
||||
|
||||
interface Address {
|
||||
key: string;
|
||||
@ -205,10 +213,16 @@ const WalletAddresses: React.FC = () => {
|
||||
centerContent={!showAddresses}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
ListHeaderComponent={
|
||||
<AddressTypeTabs
|
||||
currentTab={currentTab}
|
||||
setCurrentTab={(tab: (typeof TABS)[keyof typeof TABS]) => dispatch({ type: SET_CURRENT_TAB, payload: tab })}
|
||||
/>
|
||||
<View style={styles.segmentController}>
|
||||
<SegmentedControl
|
||||
values={Object.values(TABS).map(tab => loc.addresses[`type_${tab}`])}
|
||||
selectedIndex={Object.values(TABS).findIndex(tab => tab === currentTab)}
|
||||
onChange={(index: number) => {
|
||||
const tabKey = Object.keys(TABS)[index] as TabKey;
|
||||
dispatch({ type: SET_CURRENT_TAB, payload: TABS[tabKey] });
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
@ -221,4 +235,10 @@ const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
segmentController: {
|
||||
margin: 40,
|
||||
height: 40,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,5 @@
|
||||
import assert from 'assert';
|
||||
import { filterByAddressType, getAddress, sortByAddressIndex, totalBalance } from '../../screen/wallets/WalletAddresses';
|
||||
import { TABS } from '../../components/addresses/AddressTypeTabs';
|
||||
import { TABS, filterByAddressType, getAddress, sortByAddressIndex, totalBalance } from '../../screen/wallets/WalletAddresses';
|
||||
|
||||
jest.mock('../../blue_modules/currency', () => {
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user