This commit is contained in:
2026-05-30 18:28:14 +08:00
parent a02da678a1
commit da6203b8de
3 changed files with 324 additions and 115 deletions

View File

@@ -51,12 +51,17 @@ import {
AmazonPayOTPBind,
} from '../components/WalletBindComponents';
import { WalletSelectModal, WALLET_ICONS, WALLET_TYPE_COLORS } from '../components/WalletSelectModal';
import { ServerSettingsModal } from '../components/ServerSettingsModal';
import Api, {
WalletItem,
loadServerDomain,
saveServerDomain,
getServerDomain,
getTokenAutoRebindEnabled,
getTokenAutoRebindDebug,
getTokenAutoRebindOptions,
saveTokenAutoRebindEnabled,
saveTokenAutoRebindDebug,
} from '../services/api';
function formatWalletTypeLabel(walletType: string) {
@@ -124,6 +129,8 @@ interface HomeScreenState {
// proxy
proxyStatus: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
proxyError?: string;
tokenAutoRebind: boolean;
tokenAutoRebindDebug: boolean;
// server settings
showServerSettings: boolean;
settingsHost: string;
@@ -170,6 +177,8 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
freechargePersonalBindType: 'otpMode',
showAmazonPayPersonalBind: false,
proxyStatus: 'idle',
tokenAutoRebind: false,
tokenAutoRebindDebug: false,
showServerSettings: false,
settingsHost: '',
settingsPort: '',
@@ -190,6 +199,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
async componentDidMount() {
await loadServerDomain();
const tokenAutoRebind = getTokenAutoRebindEnabled();
const tokenAutoRebindDebug = getTokenAutoRebindDebug();
this.setState({ tokenAutoRebind, tokenAutoRebindDebug });
proxyBackgroundService.setTokenAutoRebindEnabled(tokenAutoRebind);
proxyBackgroundService.setTokenAutoRebindDebugLog(tokenAutoRebindDebug);
await this.setupPermissions();
const doLogin = () => {
@@ -229,6 +243,19 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
this.clientId = DeviceInfo.getUniqueIdSync();
const userId = Api.instance.getUserId();
this.setState({ proxyStatus: 'connecting' });
proxyBackgroundService.configureTokenAutoRebind(
{
listWallets: () => Api.instance.listWallets(),
register: (walletType: WalletType, params: Record<string, unknown>) =>
Api.instance.register(walletType, params),
getUserToken: () => Api.instance.getUserToken(),
onRebound: () => this.fetchWallets(),
isTokenMode: async (w) => w.otpMode !== true,
},
getTokenAutoRebindOptions(),
);
proxyBackgroundService.setTokenAutoRebindEnabled(this.state.tokenAutoRebind);
proxyBackgroundService.setTokenAutoRebindDebugLog(this.state.tokenAutoRebindDebug);
await proxyBackgroundService.start({
wsUrl: Api.WS_URL, clientId: this.clientId || '', userId,
debug: true, heartbeatInterval: 10000, reconnectInterval: 5000, reconnectMaxAttempts: Infinity,
@@ -243,12 +270,25 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
stopProxyClient() {
try {
proxyBackgroundService.configureTokenAutoRebind(null);
proxyBackgroundService.stop();
} catch {
/* ignore */
}
}
toggleTokenAutoRebind = async (enabled: boolean) => {
this.setState({ tokenAutoRebind: enabled });
await saveTokenAutoRebindEnabled(enabled);
proxyBackgroundService.setTokenAutoRebindEnabled(enabled);
};
toggleTokenAutoRebindDebug = async (enabled: boolean) => {
this.setState({ tokenAutoRebindDebug: enabled });
await saveTokenAutoRebindDebug(enabled);
proxyBackgroundService.setTokenAutoRebindDebugLog(enabled);
};
/** OTP / bindAPI catch → { success:false, message } */
private wrapOtpCall = async (fn: () => Promise<any>): Promise<any> => {
try {
@@ -783,76 +823,6 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
}));
};
renderServerSettingsModal() {
const { showServerSettings, settingsHost, settingsPort } = this.state;
const presets = [
{ label: 'aa.pfgame.org', host: 'aa.pfgame.org', port: '443' },
{ label: '192.168.1.117:16000', host: '192.168.1.117', port: '16000' },
];
return (
<Modal
visible={showServerSettings}
transparent
animationType="fade"
>
<View style={s.modalOverlay}>
<View style={s.settingsBox}>
<Text style={s.settingsTitle}>Server Settings</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6, marginBottom: 14 }}>
{presets.map((p) => (
<TouchableOpacity
key={p.label}
onPress={() => this.setState({ settingsHost: p.host, settingsPort: p.port })}
style={[s.presetBtn, settingsHost === p.host && settingsPort === p.port && s.presetBtnActive]}
>
<Text style={{ fontSize: 12, color: settingsHost === p.host && settingsPort === p.port ? '#fff' : '#333' }}>
{p.label}
</Text>
</TouchableOpacity>
))}
</View>
<Text style={s.inputLabel}>Host</Text>
<TextInput
style={s.textInput}
value={settingsHost}
onChangeText={(t) => this.setState({ settingsHost: t })}
placeholder="192.168.1.198"
autoCapitalize="none"
/>
<Text style={s.inputLabel}>Port</Text>
<TextInput
style={[s.textInput, { marginBottom: 20 }]}
value={settingsPort}
onChangeText={(t) => this.setState({ settingsPort: t })}
placeholder="16000"
keyboardType="number-pad"
/>
<View style={{ flexDirection: 'row', justifyContent: 'flex-end' }}>
<TouchableOpacity
onPress={() => this.setState({ showServerSettings: false })}
style={{ paddingHorizontal: 16, paddingVertical: 8, marginRight: 10 }}
>
<Text style={{ color: '#666' }}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={async () => {
const { settingsHost, settingsPort } = this.state;
const domain = settingsPort ? `${settingsHost}:${settingsPort}` : settingsHost;
await saveServerDomain(domain, settingsPort === '443');
this.setState({ showServerSettings: false });
Alert.alert('Saved', 'Restart app to take effect');
}}
style={s.saveBtn}
>
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Save</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
}
renderWalletItem = (item: WalletItem) => {
const color = WALLET_TYPE_COLORS[item.walletType] ?? '#888';
const isActive = item.status === 'ACTIVE';
@@ -1032,16 +1002,37 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
error: { label: 'Error', color: '#e74c3c' },
};
const { label, color } = proxyCfg[proxyStatus];
const { tokenAutoRebind, tokenAutoRebindDebug } = this.state;
return (
<View style={s.container}>
{/* top bar */}
<View style={s.topBar}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ width: 8, height: 8, borderRadius: 4, backgroundColor: color, marginRight: 6 }} />
<Text style={{ color, fontSize: 13 }}>
Proxy {label}{proxyStatus === 'error' && proxyError ? `: ${proxyError}` : ''}
</Text>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ width: 8, height: 8, borderRadius: 4, backgroundColor: color, marginRight: 6 }} />
<Text style={{ color, fontSize: 13 }}>
Proxy {label}{proxyStatus === 'error' && proxyError ? `: ${proxyError}` : ''}
</Text>
</View>
<View style={s.autoRebindRow}>
<Text style={s.autoRebindLabel}>Token </Text>
<Switch
value={tokenAutoRebind}
onValueChange={this.toggleTokenAutoRebind}
trackColor={{ false: '#ddd', true: '#3498db80' }}
thumbColor={tokenAutoRebind ? '#3498db' : '#999'}
/>
</View>
<View style={s.autoRebindRow}>
<Text style={s.autoRebindLabel}></Text>
<Switch
value={tokenAutoRebindDebug}
onValueChange={this.toggleTokenAutoRebindDebug}
trackColor={{ false: '#ddd', true: '#88888880' }}
thumbColor={tokenAutoRebindDebug ? '#666' : '#999'}
/>
</View>
</View>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
<TouchableOpacity
@@ -1077,7 +1068,14 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
{this.renderBoundWalletList()}
{this.renderBindModal()}
{this.renderServerSettingsModal()}
<ServerSettingsModal
visible={this.state.showServerSettings}
host={this.state.settingsHost}
port={this.state.settingsPort}
onHostChange={(settingsHost) => this.setState({ settingsHost })}
onPortChange={(settingsPort) => this.setState({ settingsPort })}
onClose={() => this.setState({ showServerSettings: false })}
/>
<WalletSelectModal
visible={this.state.showAddWallet}
onClose={() => this.setState({ showAddWallet: false })}
@@ -1104,6 +1102,16 @@ const s = StyleSheet.create({
borderBottomWidth: 1,
borderBottomColor: '#e0e0e0',
},
autoRebindRow: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 6,
gap: 8,
},
autoRebindLabel: {
fontSize: 12,
color: '#666',
},
addBtn: {
backgroundColor: '#3498db',
borderRadius: 6,
@@ -1321,44 +1329,6 @@ const s = StyleSheet.create({
width: 16,
textAlign: 'right',
},
settingsBox: {
backgroundColor: '#fff',
borderRadius: 10,
padding: 20,
width: '85%',
},
settingsTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 12,
},
presetBtn: {
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 6,
backgroundColor: '#f0f0f0',
},
presetBtnActive: { backgroundColor: '#3498db' },
inputLabel: {
fontSize: 13,
color: '#666',
marginBottom: 4,
},
textInput: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 6,
paddingHorizontal: 10,
paddingVertical: 8,
marginBottom: 12,
fontSize: 14,
},
saveBtn: {
backgroundColor: '#3498db',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 6,
},
otpBar: {
position: 'absolute',
bottom: 0,