This commit is contained in:
Matthew Fosse 2024-08-13 08:21:30 -07:00
commit 28437f1d4f
21 changed files with 169 additions and 229 deletions

View file

@ -91,5 +91,4 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.unstoppabledomains:resolution:5.0.0'
} }

View file

@ -20,14 +20,10 @@ import android.net.Uri;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
import com.unstoppabledomains.resolution.DomainResolution;
import com.unstoppabledomains.resolution.Resolution;
import java.security.SecureRandom; import java.security.SecureRandom;
public class MainActivity extends FlutterFragmentActivity { public class MainActivity extends FlutterFragmentActivity {
final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; final String UTILS_CHANNEL = "com.cake_wallet/native_utils";
final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24;
boolean isAppSecure = false; boolean isAppSecure = false;
@Override @Override
@ -53,14 +49,6 @@ public class MainActivity extends FlutterFragmentActivity {
random.nextBytes(bytes); random.nextBytes(bytes);
handler.post(() -> result.success(bytes)); handler.post(() -> result.success(bytes));
break; break;
case "getUnstoppableDomainAddress":
int version = Build.VERSION.SDK_INT;
if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) {
getUnstoppableDomainAddress(call, result);
} else {
handler.post(() -> result.success(""));
}
break;
case "setIsAppSecure": case "setIsAppSecure":
isAppSecure = call.argument("isAppSecure"); isAppSecure = call.argument("isAppSecure");
if (isAppSecure) { if (isAppSecure) {
@ -85,23 +73,6 @@ public class MainActivity extends FlutterFragmentActivity {
} }
} }
private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
DomainResolution resolution = new Resolution();
Handler handler = new Handler(Looper.getMainLooper());
String domain = call.argument("domain");
String ticker = call.argument("ticker");
AsyncTask.execute(() -> {
try {
String address = resolution.getAddress(domain, ticker);
handler.post(() -> result.success(address));
} catch (Exception e) {
System.out.println("Expected Address, but got " + e.getMessage());
handler.post(() -> result.success(""));
}
});
}
private void disableBatteryOptimization() { private void disableBatteryOptimization() {
String packageName = getPackageName(); String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);

View file

@ -19,14 +19,10 @@ import android.net.Uri;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
import com.unstoppabledomains.resolution.DomainResolution;
import com.unstoppabledomains.resolution.Resolution;
import java.security.SecureRandom; import java.security.SecureRandom;
public class MainActivity extends FlutterFragmentActivity { public class MainActivity extends FlutterFragmentActivity {
final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; final String UTILS_CHANNEL = "com.cake_wallet/native_utils";
final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24;
@Override @Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
@ -51,14 +47,6 @@ public class MainActivity extends FlutterFragmentActivity {
random.nextBytes(bytes); random.nextBytes(bytes);
handler.post(() -> result.success(bytes)); handler.post(() -> result.success(bytes));
break; break;
case "getUnstoppableDomainAddress":
int version = Build.VERSION.SDK_INT;
if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) {
getUnstoppableDomainAddress(call, result);
} else {
handler.post(() -> result.success(""));
}
break;
case "disableBatteryOptimization": case "disableBatteryOptimization":
disableBatteryOptimization(); disableBatteryOptimization();
handler.post(() -> result.success(null)); handler.post(() -> result.success(null));
@ -75,23 +63,6 @@ public class MainActivity extends FlutterFragmentActivity {
} }
} }
private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
DomainResolution resolution = new Resolution();
Handler handler = new Handler(Looper.getMainLooper());
String domain = call.argument("domain");
String ticker = call.argument("ticker");
AsyncTask.execute(() -> {
try {
String address = resolution.getAddress(domain, ticker);
handler.post(() -> result.success(address));
} catch (Exception e) {
System.out.println("Expected Address, but got " + e.getMessage());
handler.post(() -> result.success(""));
}
});
}
private void disableBatteryOptimization() { private void disableBatteryOptimization() {
String packageName = getPackageName(); String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);

View file

@ -19,14 +19,10 @@ import android.net.Uri;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
import com.unstoppabledomains.resolution.DomainResolution;
import com.unstoppabledomains.resolution.Resolution;
import java.security.SecureRandom; import java.security.SecureRandom;
public class MainActivity extends FlutterFragmentActivity { public class MainActivity extends FlutterFragmentActivity {
final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; final String UTILS_CHANNEL = "com.cake_wallet/native_utils";
final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24;
boolean isAppSecure = false; boolean isAppSecure = false;
@Override @Override
@ -52,14 +48,6 @@ public class MainActivity extends FlutterFragmentActivity {
random.nextBytes(bytes); random.nextBytes(bytes);
handler.post(() -> result.success(bytes)); handler.post(() -> result.success(bytes));
break; break;
case "getUnstoppableDomainAddress":
int version = Build.VERSION.SDK_INT;
if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) {
getUnstoppableDomainAddress(call, result);
} else {
handler.post(() -> result.success(""));
}
break;
case "setIsAppSecure": case "setIsAppSecure":
isAppSecure = call.argument("isAppSecure"); isAppSecure = call.argument("isAppSecure");
if (isAppSecure) { if (isAppSecure) {
@ -84,23 +72,6 @@ public class MainActivity extends FlutterFragmentActivity {
} }
} }
private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
DomainResolution resolution = new Resolution();
Handler handler = new Handler(Looper.getMainLooper());
String domain = call.argument("domain");
String ticker = call.argument("ticker");
AsyncTask.execute(() -> {
try {
String address = resolution.getAddress(domain, ticker);
handler.post(() -> result.success(address));
} catch (Exception e) {
System.out.println("Expected Address, but got " + e.getMessage());
handler.post(() -> result.success(""));
}
});
}
private void disableBatteryOptimization() { private void disableBatteryOptimization() {
String packageName = getPackageName(); String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);

View file

@ -2,7 +2,7 @@ buildscript {
ext.kotlin_version = '1.8.21' ext.kotlin_version = '1.8.21'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
@ -15,7 +15,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -5,7 +5,7 @@ buildscript {
ext.kotlin_version = '1.7.10' ext.kotlin_version = '1.7.10'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
@ -17,7 +17,7 @@ buildscript {
rootProject.allprojects { rootProject.allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

View file

@ -5,7 +5,7 @@ buildscript {
ext.kotlin_version = '1.7.10' ext.kotlin_version = '1.7.10'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
@ -17,7 +17,7 @@ buildscript {
rootProject.allprojects { rootProject.allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

View file

@ -36,7 +36,6 @@ target 'Runner' do
# Cake Wallet (Legacy) # Cake Wallet (Legacy)
pod 'CryptoSwift' pod 'CryptoSwift'
pod 'UnstoppableDomainsResolution', '~> 4.0.0'
end end
post_install do |installer| post_install do |installer|

View file

@ -1,6 +1,5 @@
import UIKit import UIKit
import Flutter import Flutter
import UnstoppableDomainsResolution
import workmanager import workmanager
@UIApplicationMain @UIApplicationMain
@ -87,27 +86,7 @@ import workmanager
} }
result(secRandom(count: count)) result(secRandom(count: count))
case "getUnstoppableDomainAddress":
guard let args = call.arguments as? Dictionary<String, String>,
let domain = args["domain"],
let ticker = args["ticker"],
let resolution = self?.resolution else {
result(nil)
return
}
resolution.addr(domain: domain, ticker: ticker) { addrResult in
var address : String = ""
switch addrResult {
case .success(let returnValue):
address = returnValue
case .failure(let error):
print("Expected Address, but got \(error)")
}
result(address)
}
case "setIsAppSecure": case "setIsAppSecure":
guard let args = call.arguments as? Dictionary<String, Bool>, guard let args = call.arguments as? Dictionary<String, Bool>,
let isAppSecure = args["isAppSecure"] else { let isAppSecure = args["isAppSecure"] else {

View file

@ -1,5 +1,7 @@
import 'package:cake_wallet/utils/device_info.dart'; import 'dart:convert';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
const channel = MethodChannel('com.cake_wallet/native_utils'); const channel = MethodChannel('com.cake_wallet/native_utils');
@ -7,18 +9,19 @@ Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async
var address = ''; var address = '';
try { try {
if (DeviceInfo.instance.isMobile) { final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
address = await channel.invokeMethod<String>( final jsonString = await http.read(uri);
'getUnstoppableDomainAddress', final jsonParsed = json.decode(jsonString) as Map<String, dynamic>;
<String, String> { if (jsonParsed["records"] == null) {
'domain' : domain, throw Exception(".records response from $uri is empty");
'ticker' : ticker };
} final records = jsonParsed["records"] as Map<String, dynamic>;
) ?? ''; final key = "crypto.${ticker.toUpperCase()}.address";
} else { if (records[key] == null) {
// TODO: Integrate with Unstoppable domains resolution API throw Exception(".records.${key} response from $uri is empty");
return address;
} }
return records[key] as String? ?? '';
} catch (e) { } catch (e) {
print('Unstoppable domain error: ${e.toString()}'); print('Unstoppable domain error: ${e.toString()}');
address = ''; address = '';

View file

@ -25,6 +25,7 @@ import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/seed_type.dart';
class NewWalletPage extends BasePage { class NewWalletPage extends BasePage {
NewWalletPage(this._walletNewVM, this._seedTypeViewModel); NewWalletPage(this._walletNewVM, this._seedTypeViewModel);
@ -74,6 +75,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
_walletNewVM.hasWalletPassword ? TextEditingController() : null; _walletNewVM.hasWalletPassword ? TextEditingController() : null;
static const aspectRatioImage = 1.22; static const aspectRatioImage = 1.22;
static bool formProcessing = false;
final GlobalKey<FormState> _formKey; final GlobalKey<FormState> _formKey;
final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey; final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey;
@ -347,26 +349,35 @@ class _WalletNameFormState extends State<WalletNameForm> {
); );
} }
void _confirmForm() { void _confirmForm() async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { if (formProcessing) return;
return; formProcessing = true;
} try {
if (_walletNewVM.nameExists(_walletNewVM.name)) { if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
showPopUp<void>( formProcessing = false;
context: context, return;
builder: (_) { }
return AlertWithOneAction( if (_walletNewVM.nameExists(_walletNewVM.name)) {
alertTitle: '', await showPopUp<void>(
alertContent: S.of(context).wallet_name_exists, context: context,
buttonText: S.of(context).ok, builder: (_) {
buttonAction: () => Navigator.of(context).pop()); return AlertWithOneAction(
}); alertTitle: '',
} else { alertContent: S.of(context).wallet_name_exists,
_walletNewVM.create( buttonText: S.of(context).ok,
options: _walletNewVM.hasLanguageSelector buttonAction: () => Navigator.of(context).pop());
? [_languageSelectorKey.currentState!.selected, isPolyseed] });
: null); } else {
await _walletNewVM.create(
options: _walletNewVM.hasLanguageSelector
? [_languageSelectorKey.currentState!.selected, isPolyseed]
: null);
}
} catch (e) {
formProcessing = false;
rethrow;
} }
formProcessing = false;
} }
bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed; bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed;

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@ -80,6 +81,8 @@ class WalletRestorePage extends BasePage {
}); });
} }
static bool formProcessing = false;
@override @override
Widget middle(BuildContext context) => Observer( Widget middle(BuildContext context) => Observer(
builder: (_) => Text( builder: (_) => Text(
@ -350,75 +353,85 @@ class WalletRestorePage extends BasePage {
} }
Future<void> _confirmForm(BuildContext context) async { Future<void> _confirmForm(BuildContext context) async {
// Dismissing all visible keyboard to provide context for navigation if (formProcessing) return;
FocusManager.instance.primaryFocus?.unfocus(); formProcessing = true;
try {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
late BuildContext? formContext; late BuildContext? formContext;
late GlobalKey<FormState>? formKey; late GlobalKey<FormState>? formKey;
late String name; late String name;
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) { if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
formContext = walletRestoreFromSeedFormKey.currentContext; formContext = walletRestoreFromSeedFormKey.currentContext;
formKey = walletRestoreFromSeedFormKey.currentState!.formKey; formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text; name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) { } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
formContext = walletRestoreFromKeysFormKey.currentContext; formContext = walletRestoreFromKeysFormKey.currentContext;
formKey = walletRestoreFromKeysFormKey.currentState!.formKey; formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text; name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
}
if (!formKey!.currentState!.validate()) {
return;
}
if (walletRestoreViewModel.nameExists(name)) {
showNameExistsAlert(formContext!);
return;
}
walletRestoreViewModel.state = IsExecutingState();
DerivationInfo? dInfo;
// get info about the different derivations:
List<DerivationInfo> derivations =
await walletRestoreViewModel.getDerivationInfo(_credentials());
int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0;
for (int i = 0; i < derivations.length; i++) {
if (derivations[i].transactionsCount > 0) {
derivationsWithHistory++;
derivationWithHistoryIndex = i;
} }
}
if (derivationsWithHistory > 1) { if (!formKey!.currentState!.validate()) {
dInfo = await Navigator.of(context).pushNamed( formProcessing = false;
Routes.restoreWalletChooseDerivation, return;
arguments: derivations,
) as DerivationInfo?;
} else if (derivationsWithHistory == 1) {
dInfo = derivations[derivationWithHistoryIndex];
}
// get the default derivation for this wallet type:
if (dInfo == null) {
// we only return 1 derivation if we're pretty sure we know which one to use:
if (derivations.length == 1) {
dInfo = derivations.first;
} else {
// if we have multiple possible derivations, and none have histories
// we just default to the most common one:
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
} }
}
this.derivationInfo = dInfo; if (walletRestoreViewModel.nameExists(name)) {
if (this.derivationInfo == null) { showNameExistsAlert(formContext!);
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation(); formProcessing = false;
} return;
}
walletRestoreViewModel.create(options: _credentials()); walletRestoreViewModel.state = IsExecutingState();
DerivationInfo? dInfo;
// get info about the different derivations:
List<DerivationInfo> derivations =
await walletRestoreViewModel.getDerivationInfo(_credentials());
int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0;
for (int i = 0; i < derivations.length; i++) {
if (derivations[i].transactionsCount > 0) {
derivationsWithHistory++;
derivationWithHistoryIndex = i;
}
}
if (derivationsWithHistory > 1) {
dInfo = await Navigator.of(context).pushNamed(
Routes.restoreWalletChooseDerivation,
arguments: derivations,
) as DerivationInfo?;
} else if (derivationsWithHistory == 1) {
dInfo = derivations[derivationWithHistoryIndex];
}
// get the default derivation for this wallet type:
if (dInfo == null) {
// we only return 1 derivation if we're pretty sure we know which one to use:
if (derivations.length == 1) {
dInfo = derivations.first;
} else {
// if we have multiple possible derivations, and none have histories
// we just default to the most common one:
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
}
}
this.derivationInfo = dInfo;
if (this.derivationInfo == null) {
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
}
await walletRestoreViewModel.create(options: _credentials());
} catch (e) {
formProcessing = false;
rethrow;
}
formProcessing = false;
} }
Future<void> showNameExistsAlert(BuildContext context) { Future<void> showNameExistsAlert(BuildContext context) {

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class SupportOtherLinksPage extends BasePage { class SupportOtherLinksPage extends BasePage {
@ -22,8 +23,11 @@ class SupportOtherLinksPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final iconColor = Theme.of(context).extension<SupportPageTheme>()!.iconColor; final iconColor = Theme.of(context).extension<SupportPageTheme>()!.iconColor;
final isLightMode = Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
return Container( return Container(
child: Center( child: Center(
child: ConstrainedBox( child: ConstrainedBox(
@ -37,16 +41,16 @@ class SupportOtherLinksPage extends BasePage {
if (item is RegularListItem) { if (item is RegularListItem) {
return SettingsCellWithArrow(title: item.title, handler: item.handler); return SettingsCellWithArrow(title: item.title, handler: item.handler);
} }
if (item is LinkListItem) { if (item is LinkListItem) {
bool hasLightIcon = false;
if (item.lightIcon != null) hasLightIcon = true;
return SettingsLinkProviderCell( return SettingsLinkProviderCell(
title: item.title, title: item.title,
icon: item.icon, icon: isLightMode && hasLightIcon ? item.lightIcon : item.icon,
iconColor: item.hasIconColor ? iconColor : null, iconColor: item.hasIconColor ? iconColor : null,
link: item.link, link: item.link,
linkTitle: item.linkTitle); linkTitle: item.linkTitle);
} }
return Container(); return Container();
}), }),
), ),

View file

@ -8,10 +8,12 @@ class LinkListItem extends SettingsListItem {
required this.link, required this.link,
required this.linkTitle, required this.linkTitle,
this.icon, this.icon,
this.lightIcon,
this.hasIconColor = false}) this.hasIconColor = false})
: super(title); : super(title);
final String? icon; final String? icon;
final String? lightIcon;
final String link; final String link;
final String linkTitle; final String linkTitle;
final bool hasIconColor; final bool hasIconColor;

View file

@ -33,11 +33,6 @@ abstract class SupportViewModelBase with Store {
icon: 'assets/images/Telegram.png', icon: 'assets/images/Telegram.png',
linkTitle: '@cakewallet_bot', linkTitle: '@cakewallet_bot',
link: 'https://t.me/cakewallet_bot'), link: 'https://t.me/cakewallet_bot'),
LinkListItem(
title: 'Twitter',
icon: 'assets/images/Twitter.png',
linkTitle: '@cakewallet',
link: 'https://twitter.com/cakewallet'),
LinkListItem( LinkListItem(
title: 'ChangeNow', title: 'ChangeNow',
icon: 'assets/images/change_now.png', icon: 'assets/images/change_now.png',
@ -46,7 +41,7 @@ abstract class SupportViewModelBase with Store {
LinkListItem( LinkListItem(
title: 'SideShift', title: 'SideShift',
icon: 'assets/images/sideshift.png', icon: 'assets/images/sideshift.png',
linkTitle: S.current.help, linkTitle: 'help.sideshift.ai',
link: 'https://help.sideshift.ai/en/'), link: 'https://help.sideshift.ai/en/'),
LinkListItem( LinkListItem(
title: 'SimpleSwap', title: 'SimpleSwap',
@ -58,19 +53,41 @@ abstract class SupportViewModelBase with Store {
icon: 'assets/images/exolix.png', icon: 'assets/images/exolix.png',
linkTitle: 'support@exolix.com', linkTitle: 'support@exolix.com',
link: 'mailto:support@exolix.com'), link: 'mailto:support@exolix.com'),
LinkListItem(
title: 'Quantex',
icon: 'assets/images/quantex.png',
linkTitle: 'help.myquantex.com',
link: 'mailto:support@exolix.com'),
LinkListItem(
title: 'Trocador',
icon: 'assets/images/trocador.png',
linkTitle: 'mail@trocador.app',
link: 'mailto:mail@trocador.app'),
LinkListItem(
title: 'Onramper',
icon: 'assets/images/onramper_dark.png',
lightIcon: 'assets/images/onramper_light.png',
linkTitle: 'View exchanges',
link: 'https://guides.cakewallet.com/docs/service-support/buy/#onramper'),
LinkListItem(
title: 'DFX',
icon: 'assets/images/dfx_dark.png',
lightIcon: 'assets/images/dfx_light.png',
linkTitle: 'support@dfx.swiss',
link: 'mailto:support@dfx.swiss'),
if (!isMoneroOnly) ... [ if (!isMoneroOnly) ... [
LinkListItem(
title: 'Wyre',
icon: 'assets/images/wyre.png',
linkTitle: S.current.submit_request,
link: 'https://wyre-support.zendesk.com/hc/en-us/requests/new'),
LinkListItem( LinkListItem(
title: 'MoonPay', title: 'MoonPay',
icon: 'assets/images/moonpay.png', icon: 'assets/images/moonpay.png',
hasIconColor: true,
linkTitle: S.current.submit_request, linkTitle: S.current.submit_request,
link: 'https://support.moonpay.com/hc/en-gb/requests/new') link: 'https://support.moonpay.com/hc/en-gb/requests/new'),
] LinkListItem(
title: 'Robinhood Connect',
icon: 'assets/images/robinhood_dark.png',
lightIcon: 'assets/images/robinhood_light.png',
linkTitle: S.current.submit_request,
link: 'https://robinhood.com/contact')
]
//LinkListItem( //LinkListItem(
// title: 'Yat', // title: 'Yat',
// icon: 'assets/images/yat_mini_logo.png', // icon: 'assets/images/yat_mini_logo.png',