Merge remote-tracking branch 'origin_SW/staging' into add_nano

# Conflicts:
#	lib/models/isar/stack_theme.dart
#	lib/models/isar/stack_theme.g.dart
#	lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart
#	lib/services/coins/firo/firo_wallet.dart
#	lib/themes/coin_icon_provider.dart
#	lib/themes/coin_image_provider.dart
#	lib/utilities/constants.dart
#	lib/utilities/enums/coin_enum.dart
This commit is contained in:
julian 2023-05-24 11:37:37 -06:00
commit 553e035ca7
128 changed files with 13031 additions and 4258 deletions

Binary file not shown.

Binary file not shown.

83
assets/svg/anonymize.svg Normal file
View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
version="1.1"
id="svg18"
sodipodi:docname="tx-icon-anonymize.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata22">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2262"
inkscape:window-height="1263"
id="namedview20"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="-15.813559"
inkscape:cy="12"
inkscape:window-x="1019"
inkscape:window-y="569"
inkscape:window-maximized="0"
inkscape:current-layer="g8" />
<g
clip-path="url(#clip0_5035_57299)"
id="g8">
<g
clip-path="url(#clip1_5035_57299)"
id="g6"
transform="matrix(1.6610229,0,0,1.7055051,-7.9831634,-8.2054918)">
<path
d="m 9.92728,11.6469 c 0.13122,0.5688 -0.49218,1.0145 -0.9871,0.7028 L 8.27572,11.934 6.88393,14.1606 c -0.3645,0.583 0.05479,1.3393 0.74238,1.3393 h 0.87554 c 0.48235,0 0.87446,0.3916 0.87446,0.8739 0,0.4824 -0.39211,0.8764 -0.87446,0.8764 H 7.62904 c -2.06062,0 -3.31679,-2.2651 -2.22769,-4.0141 L 6.79123,11.0098 6.12514,10.5915 C 5.62994,10.2797 5.75627,9.52505 6.32557,9.3938 L 8.8256,8.81548 C 9.06049,8.7649 9.29838,8.90709 9.35307,9.14498 Z M 12.741,7.15873 13.8689,8.96724 13.2058,9.37959 c -0.4966,0.30925 -0.3711,1.06481 0.199,1.19681 l 2.4992,0.5783 c 0.2358,0.0546 0.4712,-0.0926 0.5253,-0.3284 L 17.0046,8.32631 C 17.1356,7.75728 16.5138,7.31322 16.0183,7.62193 L 15.3522,8.03755 14.2257,6.23396 C 13.1981,4.58951 10.8023,4.58841 9.77416,6.23227 L 9.57182,6.55552 C 9.31752,6.96158 9.4433,7.50381 9.84799,7.75865 10.256,8.01456 10.7987,7.89225 11.0541,7.48412 L 11.2576,7.159 c 0.3486,-0.55754 1.1498,-0.53703 1.4834,-2.7e-4 z m 5.857,6.07957 -0.4646,-0.7454 c -0.2553,-0.4096 -0.7946,-0.5348 -1.2042,-0.2791 -0.4085,0.2548 -0.5338,0.797 -0.2784,1.2053 l 0.4646,0.7424 c 0.365,0.5829 -0.0542,1.3398 -0.7421,1.3398 h -2.6247 l 6e-4,-0.7859 c 0,-0.5846 -0.7068,-0.8774 -1.1203,-0.464 l -1.8159,1.8165 c -0.1701,0.1701 -0.1701,0.4487 2e-4,0.6188 l 1.8161,1.8139 c 0.4135,0.4129 1.1198,0.12 1.1198,-0.4643 l -7e-4,-0.7842 h 2.6212 c 2.0616,3e-4 3.3194,-2.2665 2.2284,-4.0138 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#494949" />
</g>
</g>
<defs
id="defs16">
<clipPath
id="clip0_5035_57299">
<rect
width="24"
height="24"
fill="white"
id="rect10" />
</clipPath>
<clipPath
id="clip1_5035_57299">
<rect
width="14"
height="14"
fill="white"
transform="translate(5 5)"
id="rect13" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -1 +1 @@
Subproject commit 81659ce57952c5ab54ffe6bacfbf43da159fff3e
Subproject commit 73d257ed2fe5b204cf3589822e226301b187b86d

View file

@ -41,7 +41,6 @@ class DB {
String boxNameUsedSerialsCache({required Coin coin}) =>
"${coin.name}_usedSerialsCache";
Box<dynamic>? _boxAddressBook;
Box<String>? _boxDebugInfo;
Box<NodeModel>? _boxNodeModels;
Box<NodeModel>? _boxPrimaryNodes;
@ -100,7 +99,6 @@ class DB {
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
}
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
if (Hive.isBoxOpen(boxNameNodeModels)) {

View file

@ -115,8 +115,9 @@ class CachedElectrumX {
key: groupId,
value: set);
Logging.instance.log(
"Updated currently anonymity set for ${coin.name} with group ID $groupId",
level: LogLevel.Info);
"Updated current anonymity set for ${coin.name} with group ID $groupId",
level: LogLevel.Info,
);
}
return set;
@ -187,16 +188,17 @@ class CachedElectrumX {
}
}
Future<List<dynamic>> getUsedCoinSerials({
Future<List<String>> getUsedCoinSerials({
required Coin coin,
int startNumber = 0,
}) async {
try {
List<dynamic>? cachedSerials = DB.instance.get<dynamic>(
final _list = DB.instance.get<dynamic>(
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
key: "serials") as List?;
cachedSerials ??= [];
List<String> cachedSerials =
_list == null ? [] : List<String>.from(_list);
final startNumber = cachedSerials.length;
@ -210,8 +212,9 @@ class CachedElectrumX {
);
final serials = await client.getUsedCoinSerials(startNumber: startNumber);
List newSerials = [];
for (var element in (serials["serials"] as List)) {
List<String> newSerials = [];
for (final element in (serials["serials"] as List)) {
if (!isHexadecimal(element as String)) {
newSerials.add(base64ToHex(element));
} else {
@ -221,9 +224,10 @@ class CachedElectrumX {
cachedSerials.addAll(newSerials);
await DB.instance.put<dynamic>(
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
key: "serials",
value: cachedSerials);
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
key: "serials",
value: cachedSerials,
);
return cachedSerials;
} catch (e, s) {

View file

@ -70,7 +70,7 @@ final openedFromSWBFileStringStateProvider =
// runs the MyApp widget and checks for new users, caching the value in the
// miscellaneous box for later use
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
WidgetsFlutterBinding.ensureInitialized();
GoogleFonts.config.allowRuntimeFetching = false;
if (Platform.isIOS) {
Util.libraryPath = await getLibraryDirectory();
@ -162,7 +162,7 @@ void main() async {
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
0;
if (dbVersion < Constants.currentHiveDbVersion) {
if (dbVersion < Constants.currentDataVersion) {
try {
await DbVersionMigrator().migrate(
dbVersion,
@ -178,11 +178,10 @@ void main() async {
}
}
//Add Themes directory - TODO
// await StackFileSystem.applicationThemesDirectory();
monero.onStartup();
wownero.onStartup();
if (!Platform.isLinux && !Platform.isWindows) {
wownero.onStartup();
}
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
// overlays: [SystemUiOverlay.bottom]);
@ -191,33 +190,8 @@ void main() async {
await MainDB.instance.initMainDB();
ThemeService.instance.init(MainDB.instance);
// install default themes
if (!(await ThemeService.instance.verifyInstalled(themeId: "light"))) {
Logging.instance.log(
"Installing default light theme...",
level: LogLevel.Info,
);
final lightZip = await rootBundle.load("assets/default_themes/light.zip");
await ThemeService.instance
.install(themeArchiveData: lightZip.buffer.asUint8List());
Logging.instance.log(
"Installing default light theme... finished",
level: LogLevel.Info,
);
}
if (!(await ThemeService.instance.verifyInstalled(themeId: "dark"))) {
Logging.instance.log(
"Installing default dark theme... ",
level: LogLevel.Info,
);
final darkZip = await rootBundle.load("assets/default_themes/dark.zip");
await ThemeService.instance
.install(themeArchiveData: darkZip.buffer.asUint8List());
Logging.instance.log(
"Installing default dark theme... finished",
level: LogLevel.Info,
);
}
// check and update or install default themes
await ThemeService.instance.checkDefaultThemesOnStartup();
runApp(const ProviderScope(child: MyApp()));
}

View file

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:uuid/uuid.dart';
@Deprecated("Use lib/models/isar/models/contact_entry.dart instead")
class Contact {
final String? emojiChar;
final String name;

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@Deprecated("Use lib/models/isar/models/contact_entry.dart instead")
class ContactAddressEntry {
final Coin coin;
final String address;

View file

@ -1,5 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -57,7 +57,7 @@ class AddressEntryData extends ChangeNotifier {
}
bool get isValidAddress {
if ( coin == null) {
if (coin == null) {
return true;
}
if (_address == null) {
@ -67,8 +67,11 @@ class AddressEntryData extends ChangeNotifier {
}
ContactAddressEntry buildAddressEntry() {
return ContactAddressEntry(
coin: coin!, address: address!, label: addressLabel!);
return ContactAddressEntry()
..coinName = coin!.name
..address = address!
..other = null
..label = addressLabel!;
}
@override

View file

@ -1,4 +1,5 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
part 'contact_entry.g.dart';
@ -16,9 +17,92 @@ class ContactEntry {
late final String? emojiChar;
late final String name;
late final List<String> addresses;
late final List<ContactAddressEntry> addresses;
late final bool isFavorite;
@Index(unique: true, replace: true)
late final String customId;
ContactEntry copyWith({
bool shouldCopyEmojiWithNull = false,
String? emojiChar,
String? name,
List<ContactAddressEntry>? addresses,
bool? isFavorite,
}) {
List<ContactAddressEntry> _addresses = [];
if (addresses == null) {
for (var e in this.addresses) {
_addresses.add(e.copyWith());
}
} else {
for (var e in addresses) {
_addresses.add(e.copyWith());
}
}
String? newEmoji;
if (shouldCopyEmojiWithNull) {
newEmoji = emojiChar;
} else {
newEmoji = emojiChar ?? this.emojiChar;
}
return ContactEntry(
emojiChar: newEmoji,
name: name ?? this.name,
addresses: _addresses,
isFavorite: isFavorite ?? this.isFavorite,
customId: customId,
);
}
Map<String, dynamic> toMap() {
return {
"emoji": emojiChar,
"name": name,
"addresses": addresses.map((e) => e.toMap()).toList(),
"id": customId,
"isFavorite": isFavorite,
};
}
}
@embedded
class ContactAddressEntry {
late final String coinName;
late final String address;
late final String label;
late final String? other;
@ignore
Coin get coin => Coin.values.byName(coinName);
ContactAddressEntry();
ContactAddressEntry copyWith({
Coin? coin,
String? address,
String? label,
String? other,
}) {
return ContactAddressEntry()
..coinName = coin?.name ?? coinName
..address = address ?? this.address
..label = label ?? this.label
..other = other ?? this.other;
}
Map<String, String> toMap() {
return {
"label": label,
"address": address,
"coin": coin.name,
"other": other ?? "",
};
}
@override
String toString() {
return "AddressBookEntry: ${toMap()}";
}
}

View file

@ -20,7 +20,8 @@ const ContactEntrySchema = CollectionSchema(
r'addresses': PropertySchema(
id: 0,
name: r'addresses',
type: IsarType.stringList,
type: IsarType.objectList,
target: r'ContactAddressEntry',
),
r'customId': PropertySchema(
id: 1,
@ -64,7 +65,7 @@ const ContactEntrySchema = CollectionSchema(
)
},
links: {},
embeddedSchemas: {},
embeddedSchemas: {r'ContactAddressEntry': ContactAddressEntrySchema},
getId: _contactEntryGetId,
getLinks: _contactEntryGetLinks,
attach: _contactEntryAttach,
@ -79,9 +80,11 @@ int _contactEntryEstimateSize(
var bytesCount = offsets.last;
bytesCount += 3 + object.addresses.length * 3;
{
final offsets = allOffsets[ContactAddressEntry]!;
for (var i = 0; i < object.addresses.length; i++) {
final value = object.addresses[i];
bytesCount += value.length * 3;
bytesCount +=
ContactAddressEntrySchema.estimateSize(value, offsets, allOffsets);
}
}
bytesCount += 3 + object.customId.length * 3;
@ -101,7 +104,12 @@ void _contactEntrySerialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeStringList(offsets[0], object.addresses);
writer.writeObjectList<ContactAddressEntry>(
offsets[0],
allOffsets,
ContactAddressEntrySchema.serialize,
object.addresses,
);
writer.writeString(offsets[1], object.customId);
writer.writeString(offsets[2], object.emojiChar);
writer.writeBool(offsets[3], object.isFavorite);
@ -115,7 +123,13 @@ ContactEntry _contactEntryDeserialize(
Map<Type, List<int>> allOffsets,
) {
final object = ContactEntry(
addresses: reader.readStringList(offsets[0]) ?? [],
addresses: reader.readObjectList<ContactAddressEntry>(
offsets[0],
ContactAddressEntrySchema.deserialize,
allOffsets,
ContactAddressEntry(),
) ??
[],
customId: reader.readString(offsets[1]),
emojiChar: reader.readStringOrNull(offsets[2]),
isFavorite: reader.readBool(offsets[3]),
@ -133,7 +147,13 @@ P _contactEntryDeserializeProp<P>(
) {
switch (propertyId) {
case 0:
return (reader.readStringList(offset) ?? []) as P;
return (reader.readObjectList<ContactAddressEntry>(
offset,
ContactAddressEntrySchema.deserialize,
allOffsets,
ContactAddressEntry(),
) ??
[]) as P;
case 1:
return (reader.readString(offset)) as P;
case 2:
@ -341,142 +361,6 @@ extension ContactEntryQueryWhere
extension ContactEntryQueryFilter
on QueryBuilder<ContactEntry, ContactEntry, QFilterCondition> {
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'addresses',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'addresses',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'addresses',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'addresses',
value: '',
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElementIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'addresses',
value: '',
));
});
}
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesLengthEqualTo(int length) {
return QueryBuilder.apply(this, (query) {
@ -1055,7 +939,14 @@ extension ContactEntryQueryFilter
}
extension ContactEntryQueryObject
on QueryBuilder<ContactEntry, ContactEntry, QFilterCondition> {}
on QueryBuilder<ContactEntry, ContactEntry, QFilterCondition> {
QueryBuilder<ContactEntry, ContactEntry, QAfterFilterCondition>
addressesElement(FilterQuery<ContactAddressEntry> q) {
return QueryBuilder.apply(this, (query) {
return query.object(q, r'addresses');
});
}
}
extension ContactEntryQueryLinks
on QueryBuilder<ContactEntry, ContactEntry, QFilterCondition> {}
@ -1178,12 +1069,6 @@ extension ContactEntryQuerySortThenBy
extension ContactEntryQueryWhereDistinct
on QueryBuilder<ContactEntry, ContactEntry, QDistinct> {
QueryBuilder<ContactEntry, ContactEntry, QDistinct> distinctByAddresses() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'addresses');
});
}
QueryBuilder<ContactEntry, ContactEntry, QDistinct> distinctByCustomId(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -1220,7 +1105,7 @@ extension ContactEntryQueryProperty
});
}
QueryBuilder<ContactEntry, List<String>, QQueryOperations>
QueryBuilder<ContactEntry, List<ContactAddressEntry>, QQueryOperations>
addressesProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'addresses');
@ -1251,3 +1136,673 @@ extension ContactEntryQueryProperty
});
}
}
// **************************************************************************
// IsarEmbeddedGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters
const ContactAddressEntrySchema = Schema(
name: r'ContactAddressEntry',
id: 2556413586404997281,
properties: {
r'address': PropertySchema(
id: 0,
name: r'address',
type: IsarType.string,
),
r'coinName': PropertySchema(
id: 1,
name: r'coinName',
type: IsarType.string,
),
r'label': PropertySchema(
id: 2,
name: r'label',
type: IsarType.string,
),
r'other': PropertySchema(
id: 3,
name: r'other',
type: IsarType.string,
)
},
estimateSize: _contactAddressEntryEstimateSize,
serialize: _contactAddressEntrySerialize,
deserialize: _contactAddressEntryDeserialize,
deserializeProp: _contactAddressEntryDeserializeProp,
);
int _contactAddressEntryEstimateSize(
ContactAddressEntry object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
bytesCount += 3 + object.address.length * 3;
bytesCount += 3 + object.coinName.length * 3;
bytesCount += 3 + object.label.length * 3;
{
final value = object.other;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
return bytesCount;
}
void _contactAddressEntrySerialize(
ContactAddressEntry object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.address);
writer.writeString(offsets[1], object.coinName);
writer.writeString(offsets[2], object.label);
writer.writeString(offsets[3], object.other);
}
ContactAddressEntry _contactAddressEntryDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = ContactAddressEntry();
object.address = reader.readString(offsets[0]);
object.coinName = reader.readString(offsets[1]);
object.label = reader.readString(offsets[2]);
object.other = reader.readStringOrNull(offsets[3]);
return object;
}
P _contactAddressEntryDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readString(offset)) as P;
case 1:
return (reader.readString(offset)) as P;
case 2:
return (reader.readString(offset)) as P;
case 3:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
extension ContactAddressEntryQueryFilter on QueryBuilder<ContactAddressEntry,
ContactAddressEntry, QFilterCondition> {
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'address',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'address',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'address',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
addressIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'address',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'coinName',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'coinName',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'coinName',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'coinName',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
coinNameIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'coinName',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'label',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'label',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'label',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'label',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
labelIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'label',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'other',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'other',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'other',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'other',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'other',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'other',
value: '',
));
});
}
QueryBuilder<ContactAddressEntry, ContactAddressEntry, QAfterFilterCondition>
otherIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'other',
value: '',
));
});
}
}
extension ContactAddressEntryQueryObject on QueryBuilder<ContactAddressEntry,
ContactAddressEntry, QFilterCondition> {}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,14 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/models/notification_model.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
@ -9,7 +16,7 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class NotificationCard extends StatelessWidget {
class NotificationCard extends ConsumerWidget {
const NotificationCard({
Key? key,
required this.notification,
@ -25,8 +32,17 @@ class NotificationCard extends StatelessWidget {
static const double mobileIconSize = 24;
static const double desktopIconSize = 30;
String coinIconPath(IThemeAssets assets, WidgetRef ref) {
try {
final coin = coinFromPrettyName(notification.coinName);
return ref.read(coinIconProvider(coin));
} catch (_) {
return notification.iconAssetName;
}
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final isDesktop = Util.isDesktop;
return Stack(
@ -41,8 +57,14 @@ class NotificationCard extends StatelessWidget {
child: Row(
children: [
notification.changeNowId == null
? SvgPicture.asset(
notification.iconAssetName,
? SvgPicture.file(
File(
coinIconPath(
ref.watch(
themeAssetsProvider,
),
ref),
),
width: isDesktop ? desktopIconSize : mobileIconSize,
height: isDesktop ? desktopIconSize : mobileIconSize,
)
@ -53,8 +75,14 @@ class NotificationCard extends StatelessWidget {
color: Colors.transparent,
borderRadius: BorderRadius.circular(24),
),
child: SvgPicture.asset(
notification.iconAssetName,
child: SvgPicture.file(
File(
coinIconPath(
ref.watch(
themeAssetsProvider,
),
ref),
),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,

View file

@ -120,6 +120,8 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
if (Platform.isWindows) {
_coins.remove(Coin.monero);
_coins.remove(Coin.wownero);
} else if (Platform.isLinux) {
_coins.remove(Coin.wownero);
}
coinEntities.addAll(_coins.map((e) => CoinEntity(e)));

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
@ -73,19 +72,18 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
final managers = ref.read(walletsChangeNotifierProvider).managers;
for (final manager in managers) {
addresses.add(
ContactAddressEntry(
coin: manager.coin,
address: await manager.currentReceivingAddress,
label: "Current Receiving",
other: manager.walletName,
),
ContactAddressEntry()
..coinName = manager.coin.name
..address = await manager.currentReceivingAddress
..label = "Current Receiving"
..other = manager.walletName,
);
}
final self = Contact(
final self = ContactEntry(
name: "My Stack",
addresses: addresses,
isFavorite: true,
id: "default",
customId: "default",
);
await ref.read(addressBookServiceProvider).editContact(self);
});
@ -102,7 +100,8 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final contacts = ref.watch(addressBookServiceProvider.select((value) => value.contacts));
final contacts =
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
final isDesktop = Util.isDesktop;
return ConditionalParent(
@ -305,8 +304,8 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
.where((element) => element.isFavorite)
.map(
(e) => AddressBookCard(
key: Key("favContactCard_${e.id}_key"),
contactId: e.id,
key: Key("favContactCard_${e.customId}_key"),
contactId: e.customId,
),
),
],
@ -351,8 +350,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
.matches(widget.filterTerm ?? _searchTerm, e))
.map(
(e) => AddressBookCard(
key: Key("desktopContactCard_${e.id}_key"),
contactId: e.id,
key:
Key("desktopContactCard_${e.customId}_key"),
contactId: e.customId,
),
),
],

View file

@ -2,8 +2,7 @@ import 'package:emojis/emoji.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/new_contact_address_entry_form.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
@ -28,6 +27,7 @@ import 'package:stackwallet/widgets/emoji_select_sheet.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:uuid/uuid.dart';
class AddAddressBookEntryView extends ConsumerStatefulWidget {
const AddAddressBookEntryView({
@ -688,11 +688,12 @@ class _AddAddressBookEntryViewState
forms[i].id))
.buildAddressEntry());
}
Contact contact = Contact(
ContactEntry contact = ContactEntry(
emojiChar: _selectedEmoji?.char,
name: nameController.text,
addresses: entries,
isFavorite: _isFavorite,
customId: const Uuid().v1(),
);
if (await ref

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/new_contact_address_entry_form.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
@ -208,7 +207,7 @@ class _AddNewContactAddressViewState
.read(addressEntryDataProvider(0))
.buildAddressEntry());
Contact editedContact =
ContactEntry editedContact =
contact.copyWith(addresses: entries);
if (await ref

View file

@ -1,9 +1,11 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_image_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -87,8 +89,10 @@ class CoinSelectSheet extends StatelessWidget {
padding: const EdgeInsets.all(12),
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinImageProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinImageProvider(coin)),
),
height: 20,
width: 20,
),

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -13,8 +15,8 @@ import 'package:stackwallet/providers/global/address_book_service_provider.dart'
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -197,7 +199,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
onPressed: () {
ref
.read(addressBookServiceProvider)
.removeContact(_contact.id);
.removeContact(_contact.customId);
Navigator.of(context).pop();
Navigator.of(context).pop();
showFloatingFlushBar(
@ -268,7 +270,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
onPressed: () {
Navigator.of(context).pushNamed(
EditContactNameEmojiView.routeName,
arguments: _contact.id,
arguments: _contact.customId,
);
},
style: Theme.of(context)
@ -316,7 +318,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
onTap: () {
Navigator.of(context).pushNamed(
AddNewContactAddressView.routeName,
arguments: _contact.id,
arguments: _contact.customId,
);
},
),
@ -334,8 +336,10 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
padding: const EdgeInsets.all(12),
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(e.coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(e.coin)),
),
height: 24,
),
const SizedBox(
@ -381,7 +385,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
Navigator.of(context).pushNamed(
EditContactAddressView.routeName,
arguments: Tuple2(_contact.id, e),
arguments: Tuple2(_contact.customId, e),
);
},
child: RoundedContainer(

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -9,8 +11,8 @@ import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.
import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -108,13 +110,15 @@ class ContactPopUp extends ConsumerWidget {
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(32),
),
child: contact.id == "default"
child: contact.customId == "default"
? Center(
child: SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) =>
value.assets.stackIcon,
child: SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value
.assets.stackIcon,
),
),
),
width: 20,
@ -142,13 +146,13 @@ class ContactPopUp extends ConsumerWidget {
STextStyles.itemSubtitle12(context),
),
),
if (contact.id != "default")
if (contact.customId != "default")
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.of(context).pushNamed(
ContactDetailsView.routeName,
arguments: contact.id,
arguments: contact.customId,
);
},
style: Theme.of(context)
@ -172,7 +176,7 @@ class ContactPopUp extends ConsumerWidget {
),
),
SizedBox(
height: contact.id == "default" ? 16 : 8,
height: contact.customId == "default" ? 16 : 8,
),
Container(
height: 1,
@ -211,9 +215,11 @@ class ContactPopUp extends ConsumerWidget {
const SizedBox(
height: 2,
),
SvgPicture.asset(
ref.watch(
coinIconProvider(e.coin),
SvgPicture.file(
File(
ref.watch(
coinIconProvider(e.coin),
),
),
height: 24,
),
@ -227,14 +233,14 @@ class ContactPopUp extends ConsumerWidget {
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
if (contact.id == "default")
if (contact.customId == "default")
Text(
e.other!,
style:
STextStyles.itemSubtitle12(
context),
),
if (contact.id != "default")
if (contact.customId != "default")
Text(
"${e.label} (${e.coin.ticker})",
style:
@ -330,13 +336,13 @@ class ContactPopUp extends ConsumerWidget {
),
],
),
if (contact.id != "default" &&
if (contact.customId != "default" &&
hasActiveWallet &&
!isExchangeFlow)
const SizedBox(
width: 4,
),
if (contact.id != "default" &&
if (contact.customId != "default" &&
hasActiveWallet &&
!isExchangeFlow)
Column(

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/new_contact_address_entry_form.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
@ -49,7 +48,7 @@ class _EditContactAddressViewState
late final BarcodeScannerInterface barcodeScanner;
late final ClipboardInterface clipboard;
Future<void> save(Contact contact) async {
Future<void> save(ContactEntry contact) async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
@ -73,7 +72,7 @@ class _EditContactAddressViewState
entries.insert(index, editedEntry);
Contact editedContact = contact.copyWith(addresses: entries);
ContactEntry editedContact = contact.copyWith(addresses: entries);
if (await ref.read(addressBookServiceProvider).editContact(editedContact)) {
if (mounted) {
@ -226,7 +225,8 @@ class _EditContactAddressViewState
);
_addresses.remove(entry);
Contact editedContact = contact.copyWith(addresses: _addresses);
ContactEntry editedContact =
contact.copyWith(addresses: _addresses);
if (await ref
.read(addressBookServiceProvider)
.editContact(editedContact)) {

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -7,8 +9,8 @@ import 'package:stackwallet/pages/address_book_views/subviews/coin_select_sheet.
import 'package:stackwallet/providers/providers.dart';
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
@ -141,8 +143,10 @@ class _NewContactAddressEntryFormState
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
height: 24,
width: 24,
),
@ -211,15 +215,17 @@ class _NewContactAddressEntryFormState
)
: Row(
children: [
SvgPicture.asset(
ref.watch(
coinIconProvider(
ref.watch(
addressEntryDataProvider(widget.id)
.select(
(value) => value.coin,
),
)!,
SvgPicture.file(
File(
ref.watch(
coinIconProvider(
ref.watch(
addressEntryDataProvider(widget.id)
.select(
(value) => value.coin,
),
)!,
),
),
),
height: 20,

View file

@ -113,7 +113,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
super.initState();
}
String _fetchIconAssetForStatus(String statusString, ThemeAssets assets) {
String _fetchIconAssetForStatus(String statusString, IThemeAssets assets) {
ChangeNowTransactionStatus? status;
try {
if (statusString.toLowerCase().startsWith("waiting")) {
@ -322,11 +322,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
File(
_fetchIconAssetForStatus(
trade.status,
ref.watch(
themeProvider.select(
(value) => value.assets,
),
),
ref.watch(themeAssetsProvider),
),
),
width: 32,
@ -393,11 +389,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
File(
_fetchIconAssetForStatus(
trade.status,
ref.watch(
themeProvider.select(
(value) => value.assets,
),
),
ref.watch(themeAssetsProvider),
),
),
width: 32,
@ -1232,7 +1224,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
if (trade.exchangeName
.startsWith(TrocadorExchange.exchangeName)) {
url =
"https://trocador.app/en/checkout${trade.tradeId}";
"https://trocador.app/en/checkout/${trade.tradeId}";
}
}
return ConditionalParent(

View file

@ -138,10 +138,8 @@ class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
const Spacer(
flex: 1,
),
Image(
image: AssetImage(
Assets.svg.unclaimedPaynym,
),
SvgPicture.asset(
Assets.svg.unclaimedPaynym,
width: MediaQuery.of(context).size.width / 2,
),
const SizedBox(

View file

@ -144,7 +144,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
aspectRatio: 1,
child: AppBarIconButton(
semanticsLabel:
"Address List Pop-up Button. Opens A Pop-up For Adress List Button.",
"Address List Pop-up Button. Opens A Pop-up For Address List Button.",
key: const Key("walletNetworkSettingsAddNewNodeViewButton"),
size: 36,
shadows: const [],

View file

@ -35,6 +35,12 @@ class _ManageExplorerViewState extends ConsumerState<ManageExplorerView> {
.replaceAll("%5BTXID%5D", "[TXID]"));
}
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Background(
@ -93,13 +99,19 @@ class _ManageExplorerViewState extends ConsumerState<ManageExplorerView> {
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () {
onPressed: () async {
textEditingController.text =
textEditingController.text.trim();
setBlockExplorerForCoin(
coin: widget.coin,
url: Uri.parse(textEditingController.text))
.then((value) => Navigator.of(context).pop());
await setBlockExplorerForCoin(
coin: widget.coin,
url: Uri.parse(
textEditingController.text,
),
);
if (mounted) {
Navigator.of(context).pop();
}
},
child: Text(
"Save",

View file

@ -225,14 +225,14 @@ class _IncognitoInstalledThemesState
extends ConsumerState<IncognitoInstalledThemes> {
late final StreamSubscription<void> _subscription;
List<Tuple2<String, String>> installedThemeIdNames = [];
List<Tuple3<String, String, int?>> installedThemeIdNames = [];
void _updateInstalledList() {
installedThemeIdNames = ref
.read(pThemeService)
.installedThemes
.where((e) => e.themeId != "light" && e.themeId != "dark")
.map((e) => Tuple2(e.themeId, e.name))
.map((e) => Tuple3(e.themeId, e.name, e.version))
.toList();
}
@ -274,6 +274,7 @@ class _IncognitoInstalledThemesState
data: StackThemeMetaData(
name: e.item2,
id: e.item1,
version: e.item3 ?? 1,
sha256: "",
size: "",
previewImageUrl: "",

View file

@ -39,6 +39,7 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
late final StreamSubscription<void> _subscription;
late bool _hasTheme;
bool _needsUpdate = false;
String? _cachedSize;
Future<bool> _downloadAndInstall() async {
@ -84,6 +85,7 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
title: message,
onOkPressed: (_) {
setState(() {
_needsUpdate = !result;
_hasTheme = result;
});
},
@ -141,16 +143,21 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
}
}
StackTheme? getInstalled() => ref
.read(mainDBProvider)
.isar
.stackThemes
.where()
.themeIdEqualTo(widget.data.id)
.findFirstSync();
@override
void initState() {
_hasTheme = ref
.read(mainDBProvider)
.isar
.stackThemes
.where()
.themeIdEqualTo(widget.data.id)
.countSync() >
0;
final installedTheme = getInstalled();
_hasTheme = installedTheme != null;
if (_hasTheme) {
_needsUpdate = widget.data.version > (installedTheme?.version ?? 0);
}
_subscription = ref
.read(mainDBProvider)
@ -158,18 +165,16 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
.stackThemes
.watchLazy()
.listen((event) async {
final hasTheme = (await ref
.read(mainDBProvider)
.isar
.stackThemes
.where()
.themeIdEqualTo(widget.data.id)
.count()) >
0;
final installedTheme = getInstalled();
final hasTheme = installedTheme != null;
if (_hasTheme != hasTheme && mounted) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
_hasTheme = hasTheme;
if (hasTheme) {
_needsUpdate =
widget.data.version > (installedTheme.version ?? 0);
}
});
});
}
@ -272,6 +277,16 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
}
},
),
if (_hasTheme && _needsUpdate)
const SizedBox(
height: 12,
),
if (_hasTheme && _needsUpdate)
PrimaryButton(
label: "Update",
buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.l,
onPressed: _downloadPressed,
),
const SizedBox(
height: 12,
),

View file

@ -191,7 +191,8 @@ class GlobalSettingsView extends StatelessWidget {
title: "Appearance",
onPressed: () {
Navigator.of(context).pushNamed(
AppearanceSettingsView.routeName);
AppearanceSettingsView.routeName,
);
},
),
if (Platform.isIOS)

View file

@ -718,6 +718,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
case Coin.dogecoinTestNet:
case Coin.epicCash:
case Coin.nano:
case Coin.eCash:
return false;
case Coin.ethereum:

View file

@ -5,10 +5,9 @@ import 'dart:typed_data';
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/stack_restoring_ui_state.dart';
import 'package:stackwallet/models/trade_wallet_lookup.dart';
@ -266,7 +265,7 @@ abstract class SWB {
);
AddressBookService addressBookService = AddressBookService();
var addresses = await addressBookService.addressBookEntries;
var addresses = addressBookService.contacts;
backupJson['addressBookEntries'] =
addresses.map((e) => e.toMap()).toList();
@ -799,7 +798,7 @@ abstract class SWB {
// contacts
final addressBookService = AddressBookService();
final allContactIds = addressBookService.contacts.map((e) => e.id);
final allContactIds = addressBookService.contacts.map((e) => e.customId);
if (addressBookEntries == null) {
// if no contacts were present before attempted restore then delete any that
@ -823,21 +822,20 @@ abstract class SWB {
List<ContactAddressEntry> addresses = [];
for (var address in (contact['addresses'] as List<dynamic>)) {
addresses.add(
ContactAddressEntry(
coin: Coin.values
.firstWhere((element) => element.name == address['coin']),
address: address['address'] as String,
label: address['label'] as String,
),
ContactAddressEntry()
..coinName = address['coin'] as String
..address = address['address'] as String
..label = address['label'] as String
..other = address['other'] as String?,
);
}
await addressBookService.editContact(
Contact(
ContactEntry(
emojiChar: contact['emoji'] as String?,
name: contact['name'] as String,
addresses: addresses,
isFavorite: contact['isFavorite'] as bool,
id: contact['id'] as String,
customId: contact['id'] as String,
),
);
} else {
@ -1026,21 +1024,20 @@ abstract class SWB {
List<ContactAddressEntry> addresses = [];
for (var address in (contact['addresses'] as List<dynamic>)) {
addresses.add(
ContactAddressEntry(
coin: Coin.values
.firstWhere((element) => element.name == address['coin']),
address: address['address'] as String,
label: address['label'] as String,
),
ContactAddressEntry()
..coinName = address['coin'] as String
..address = address['address'] as String
..label = address['label'] as String
..other = address['other'] as String?,
);
}
await addressBookService.addContact(
Contact(
ContactEntry(
emojiChar: contact['emoji'] as String?,
name: contact['name'] as String,
addresses: addresses,
isFavorite: contact['isFavorite'] as bool,
id: contact['id'] as String,
customId: contact['id'] as String,
),
);
}

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -6,8 +8,8 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart';
import 'package:stackwallet/providers/stack_restore/stack_restoring_ui_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/stack_restoring_status.dart';
@ -81,8 +83,10 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
.extension<StackColors>()!
.colorForCoin(coin),
child: Center(
child: SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
child: SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
height: 20,
width: 20,
),
@ -222,9 +226,11 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
.extension<StackColors>()!
.colorForCoin(coin),
child: Center(
child: SvgPicture.asset(
ref.watch(
coinIconProvider(coin),
child: SvgPicture.file(
File(
ref.watch(
coinIconProvider(coin),
),
),
height: 20,
width: 20,

View file

@ -1,10 +1,12 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/background.dart';
@ -236,22 +238,24 @@ class _StartupPreferencesViewState
.only(top: 12),
child: Row(
children: [
SvgPicture.asset(
ref.watch(
coinIconProvider(
ref
.watch(
walletsChangeNotifierProvider
.select(
(value) =>
value.getManager(
ref.watch(
prefsChangeNotifierProvider.select((value) => value.startupWalletId!),
SvgPicture.file(
File(
ref.watch(
coinIconProvider(
ref
.watch(
walletsChangeNotifierProvider
.select(
(value) =>
value.getManager(
ref.watch(
prefsChangeNotifierProvider.select((value) => value.startupWalletId!),
),
),
),
),
)
.coin,
)
.coin,
),
),
),
),

View file

@ -1,9 +1,11 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/background.dart';
@ -102,9 +104,11 @@ class _StartupWalletSelectionViewState
),
child: Padding(
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(
ref.watch(
coinIconProvider(manager.coin),
child: SvgPicture.file(
File(
ref.watch(
coinIconProvider(manager.coin),
),
),
width: 20,
height: 20,

View file

@ -190,7 +190,7 @@ class AboutItem extends StatelessWidget {
height: iconSize,
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon,
.topNavIconPrimary,
),
),
const SizedBox(

View file

@ -1,9 +1,11 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
@ -116,9 +118,11 @@ class WalletSyncingOptionsView extends ConsumerWidget {
),
child: Padding(
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(
ref.watch(
coinIconProvider(manager.coin),
child: SvgPicture.file(
File(
ref.watch(
coinIconProvider(manager.coin),
),
),
width: 20,
height: 20,

View file

@ -8,7 +8,6 @@ import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/mixins/xpubable.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -177,7 +176,7 @@ class _XPubViewState extends ConsumerState<XPubView> {
child: child,
),
child: FutureBuilder(
future: (manager.wallet as XPubAble).xpub,
future: manager.xpub,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {

View file

@ -33,7 +33,7 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:tuple/tuple.dart';
/// [eventBus] should only be set during testing
class WalletSettingsView extends StatefulWidget {
class WalletSettingsView extends ConsumerStatefulWidget {
const WalletSettingsView({
Key? key,
required this.walletId,
@ -52,10 +52,10 @@ class WalletSettingsView extends StatefulWidget {
final EventBus? eventBus;
@override
State<WalletSettingsView> createState() => _WalletSettingsViewState();
ConsumerState<WalletSettingsView> createState() => _WalletSettingsViewState();
}
class _WalletSettingsViewState extends State<WalletSettingsView> {
class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
late final String walletId;
late final Coin coin;
late String xpub;
@ -74,7 +74,7 @@ class _WalletSettingsViewState extends State<WalletSettingsView> {
walletId = widget.walletId;
coin = widget.coin;
xPubEnabled =
coin != Coin.monero && coin != Coin.wownero && coin != Coin.epicCash;
ref.read(walletsChangeNotifierProvider).getManager(walletId).hasXPub;
xpub = "";
_currentSyncStatus = widget.initialSyncStatus;

View file

@ -308,14 +308,23 @@ class TokenOptionsButton extends StatelessWidget {
child: child,
),
),
child: SvgPicture.file(
File(iconAssetPathSVG),
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryIcon,
width: iconSize,
height: iconSize,
),
child: iconAssetPathSVG.startsWith("assets/")
? SvgPicture.asset(
iconAssetPathSVG,
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryIcon,
width: iconSize,
height: iconSize,
)
: SvgPicture.file(
File(iconAssetPathSVG),
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryIcon,
width: iconSize,
height: iconSize,
),
),
),
),

View file

@ -23,21 +23,8 @@ class TxIcon extends ConsumerWidget {
static const Size size = Size(32, 32);
// String _getBundleAssetName(
// bool isCancelled, bool isReceived, bool isPending, BuildContext context) {
// if (!isReceived && transaction.subType == TransactionSubType.mint) {
// if (isCancelled) {
// return Assets.svg.anonymizeFailed;
// }
// if (isPending) {
// return Assets.svg.anonymizePending;
// }
// return Assets.svg.anonymize;
// }
// }
String _getAssetName(
bool isCancelled, bool isReceived, bool isPending, ThemeAssets assets) {
bool isCancelled, bool isReceived, bool isPending, IThemeAssets assets) {
if (!isReceived && transaction.subType == TransactionSubType.mint) {
if (isCancelled) {
return Assets.svg.anonymizeFailed;
@ -78,7 +65,7 @@ class TxIcon extends ConsumerWidget {
currentHeight,
coin.requiredConfirmations,
),
ref.watch(themeProvider).assets,
ref.watch(themeAssetsProvider),
);
return SizedBox(

View file

@ -3,8 +3,8 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/models/transaction_filter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
@ -120,8 +120,8 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
}).toList();
}
bool _isKeywordMatch(Transaction tx, String keyword, List<Contact> contacts,
Map<String, String> notes) {
bool _isKeywordMatch(Transaction tx, String keyword,
List<ContactEntry> contacts, Map<String, String> notes) {
if (keyword.isEmpty) {
return true;
}

View file

@ -1,11 +1,13 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/dialogs/cancelling_transaction_progress_dialog.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart';
@ -605,17 +607,66 @@ class _TransactionDetailsViewState
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
_transaction.type ==
TransactionType.outgoing
? "Sent to"
: "Receiving address",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(
context),
ConditionalParent(
condition: kDebugMode,
builder: (child) {
return Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
child,
CustomTextButton(
text: "Info",
onTap: () {
if (isDesktop) {
showDialog<void>(
context: context,
builder: (_) =>
DesktopDialog(
maxHeight:
double.infinity,
child:
AddressDetailsView(
addressId:
_transaction
.address
.value!
.id,
walletId: widget
.walletId,
),
),
);
} else {
Navigator.of(context)
.pushNamed(
AddressDetailsView
.routeName,
arguments: Tuple2(
_transaction.address
.value!.id,
widget.walletId,
),
);
}
},
)
],
);
},
child: Text(
_transaction.type ==
TransactionType.outgoing
? "Sent to"
: "Receiving address",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(
context),
),
),
const SizedBox(
height: 8,
@ -1099,6 +1150,46 @@ class _TransactionDetailsViewState
],
),
),
if (kDebugMode)
isDesktop
? const _Divider()
: const SizedBox(
height: 12,
),
if (kDebugMode)
RoundedWhiteContainer(
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Tx sub type",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(context),
),
SelectableText(
_transaction.subType.toString(),
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
)
: STextStyles.itemSubtitle12(context),
),
],
),
),
isDesktop
? const _Divider()
: const SizedBox(

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart';
import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart';
@ -105,19 +104,18 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
final managers = ref.read(walletsChangeNotifierProvider).managers;
for (final manager in managers) {
addresses.add(
ContactAddressEntry(
coin: manager.coin,
address: await manager.currentReceivingAddress,
label: "Current Receiving",
other: manager.walletName,
),
ContactAddressEntry()
..coinName = manager.coin.name
..address = await manager.currentReceivingAddress
..label = "Current Receiving"
..other = manager.walletName,
);
}
final self = Contact(
final self = ContactEntry(
name: "My Stack",
addresses: addresses,
isFavorite: true,
id: "default",
customId: "default",
);
await ref.read(addressBookServiceProvider).editContact(self);
});
@ -323,14 +321,14 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
.extension<StackColors>()!
.accentColorDark
.withOpacity(
currentContactId == favorites[i].id
currentContactId == favorites[i].customId
? 0.08
: 0,
),
child: RawMaterialButton(
onPressed: () {
setState(() {
currentContactId = favorites[i].id;
currentContactId = favorites[i].customId;
});
},
padding: const EdgeInsets.symmetric(
@ -346,8 +344,8 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
child: AddressBookCard(
key: Key(
"favContactCard_${favorites[i].id}_key"),
contactId: favorites[i].id,
"favContactCard_${favorites[i].customId}_key"),
contactId: favorites[i].customId,
desktopSendFrom: false,
),
),
@ -393,14 +391,16 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
.extension<StackColors>()!
.accentColorDark
.withOpacity(
currentContactId == allContacts[i].id
currentContactId ==
allContacts[i].customId
? 0.08
: 0,
),
child: RawMaterialButton(
onPressed: () {
setState(() {
currentContactId = allContacts[i].id;
currentContactId =
allContacts[i].customId;
});
},
padding: const EdgeInsets.symmetric(
@ -416,8 +416,8 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
child: AddressBookCard(
key: Key(
"favContactCard_${allContacts[i].id}_key"),
contactId: allContacts[i].id,
"favContactCard_${allContacts[i].customId}_key"),
contactId: allContacts[i].customId,
desktopSendFrom: false,
),
),

View file

@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';

View file

@ -1,8 +1,11 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart';
import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart';
@ -24,8 +27,6 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';
import '../../../db/isar/main_db.dart';
class DesktopContactDetails extends ConsumerStatefulWidget {
const DesktopContactDetails({
Key? key,
@ -42,7 +43,7 @@ class DesktopContactDetails extends ConsumerStatefulWidget {
class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
List<Tuple2<String, Transaction>> _cachedTransactions = [];
bool _contactHasAddress(String address, Contact contact) {
bool _contactHasAddress(String address, ContactEntry contact) {
for (final entry in contact.addresses) {
if (entry.address == address) {
return true;
@ -82,7 +83,7 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
@override
Widget build(BuildContext context) {
// provider hack to prevent trying to update widget with deleted contact
Contact? _contact;
ContactEntry? _contact;
try {
_contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(widget.contactId)));
@ -110,19 +111,21 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
width: 32,
height: 32,
decoration: BoxDecoration(
color: contact.id == "default"
color: contact.customId == "default"
? Colors.transparent
: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(32),
),
child: contact.id == "default"
child: contact.customId == "default"
? Center(
child: SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
child: SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
width: 32,
@ -159,7 +162,7 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
barrierColor: Colors.transparent,
builder: (context) {
return DesktopContactOptionsMenuPopup(
contactId: contact.id,
contactId: contact.customId,
);
},
);
@ -261,7 +264,7 @@ class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
padding: const EdgeInsets.all(18),
child: DesktopAddressCard(
entry: contact.addresses[i],
contactId: contact.id,
contactId: contact.customId,
),
),
],

View file

@ -147,7 +147,7 @@ class _DesktopContactOptionsMenuPopupState
onPressed: () {
ref
.read(addressBookServiceProvider)
.removeContact(contact.id);
.removeContact(contact.customId);
Navigator.of(context).pop();
showFloatingFlushBar(
type: FlushBarType.success,

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -6,8 +8,8 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/utxo_row.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -359,8 +361,10 @@ class _DesktopCoinControlUseDialogState
color: Colors.transparent,
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
width: 24,
height: 24,
),

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -7,8 +9,8 @@ import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/freeze_button.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/utxo_row.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -343,8 +345,10 @@ class _DesktopCoinControlViewState
color: Colors.transparent,
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
width: 24,
height: 24,
),

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
@ -287,7 +288,7 @@ class DesktopTradeRowCard extends ConsumerStatefulWidget {
class _DesktopTradeRowCardState extends ConsumerState<DesktopTradeRowCard> {
late final String tradeId;
String _fetchIconAssetForStatus(String statusString, ThemeAssets assets) {
String _fetchIconAssetForStatus(String statusString, IThemeAssets assets) {
ChangeNowTransactionStatus? status;
try {
if (statusString.toLowerCase().startsWith("waiting")) {
@ -522,10 +523,14 @@ class _DesktopTradeRowCardState extends ConsumerState<DesktopTradeRowCard> {
borderRadius: BorderRadius.circular(32),
),
child: Center(
child: SvgPicture.asset(
_fetchIconAssetForStatus(
trade.status,
ref.watch(themeProvider.select((value) => value.assets)),
child: SvgPicture.file(
File(
_fetchIconAssetForStatus(
trade.status,
ref.watch(
themeAssetsProvider,
),
),
),
width: 32,
height: 32,

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -61,8 +63,8 @@ class DesktopBuyIcon extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SvgPicture.asset(
ref.watch(themeProvider.select((value) => value.assets.buy)),
return SvgPicture.file(
File(ref.watch(themeAssetsProvider).buy),
width: 20,
height: 20,
color: DesktopMenuItemId.buy ==
@ -81,28 +83,36 @@ class DesktopNotificationsIcon extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SvgPicture.asset(
ref.watch(notificationsProvider
.select((value) => value.hasUnreadNotifications))
? ref.watch(
themeProvider.select(
(value) => value.assets.bellNew,
return ref.watch(notificationsProvider
.select((value) => value.hasUnreadNotifications))
? SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.bellNew,
),
),
)
: Assets.svg.bell,
width: 20,
height: 20,
color: ref.watch(notificationsProvider
.select((value) => value.hasUnreadNotifications))
? null
: DesktopMenuItemId.notifications ==
ref.watch(currentDesktopMenuItemProvider.state).state
? Theme.of(context).extension<StackColors>()!.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.8),
);
),
width: 20,
height: 20,
)
: SvgPicture.asset(
Assets.svg.bell,
width: 20,
height: 20,
color: ref.watch(notificationsProvider
.select((value) => value.hasUnreadNotifications))
? null
: DesktopMenuItemId.notifications ==
ref.watch(currentDesktopMenuItemProvider.state).state
? Theme.of(context)
.extension<StackColors>()!
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.8),
);
}
}

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -53,10 +55,12 @@ class DesktopMyStackTitle extends ConsumerWidget {
SizedBox(
width: 32,
height: 32,
child: SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
child: SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
),

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -10,8 +12,8 @@ import 'package:stackwallet/providers/global/price_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -81,8 +83,10 @@ class _DesktopPaynymSendDialogState
// Theme.of(context).extension<StackColors>()!.textSubtitle4,
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
width: 36,
height: 36,
),

View file

@ -1,10 +1,12 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/wallets_view/wallets_overview.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -141,8 +143,10 @@ class _DesktopWalletSummaryRowState
flex: 4,
child: Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(widget.coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(widget.coin)),
),
width: 28,
height: 28,
),

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -34,7 +34,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
String _searchTerm = "";
int _compareContactFavorite(Contact a, Contact b) {
int _compareContactFavorite(ContactEntry a, ContactEntry b) {
if (a.isFavorite && b.isFavorite) {
return 0;
} else if (a.isFavorite) {
@ -44,8 +44,8 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
}
}
List<Contact> pullOutFavorites(List<Contact> contacts) {
final List<Contact> favorites = [];
List<ContactEntry> pullOutFavorites(List<ContactEntry> contacts) {
final List<ContactEntry> favorites = [];
contacts.removeWhere((contact) {
if (contact.isFavorite) {
favorites.add(contact);
@ -57,7 +57,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
return favorites;
}
List<Contact> filter(List<Contact> contacts, String searchTerm) {
List<ContactEntry> filter(List<ContactEntry> contacts, String searchTerm) {
if (widget.coin != null) {
contacts.removeWhere(
(e) => e.addresses.where((a) => a.coin == widget.coin!).isEmpty);
@ -75,7 +75,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
return contacts;
}
bool _matches(String term, Contact contact) {
bool _matches(String term, ContactEntry contact) {
final text = term.toLowerCase();
if (contact.name.toLowerCase().contains(text)) {
return true;
@ -191,7 +191,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
),
child: Consumer(
builder: (context, ref, _) {
List<Contact> contacts = ref
List<ContactEntry> contacts = ref
.watch(addressBookServiceProvider
.select((value) => value.contacts))
.toList();
@ -228,7 +228,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
),
);
} else if (index < favorites.length + 1) {
final id = favorites[index - 1].id;
final id = favorites[index - 1].customId;
return ContactListItem(
key: Key("contactContactListItem_${id}_key"),
contactId: id,
@ -248,7 +248,8 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
),
);
} else {
final id = contacts[index - favorites.length - 2].id;
final id =
contacts[index - favorites.length - 2].customId;
return ContactListItem(
key: Key("contactContactListItem_${id}_key"),
contactId: id,

View file

@ -69,7 +69,7 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
_MoreFeaturesItem(
label: "Anonymize funds",
detail: "Anonymize funds",
iconAsset: Assets.svg.anonymize,
iconAsset: Assets.svg.recycle,
onPressed: () => widget.onAnonymizeAllPressed?.call(),
),
if (manager.hasWhirlpoolSupport)

View file

@ -94,10 +94,12 @@ class _ForgotPasswordDesktopViewState
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
width: 100,

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -54,7 +55,7 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
0;
if (dbVersion < Constants.currentHiveDbVersion) {
if (dbVersion < Constants.currentDataVersion) {
try {
await DbVersionMigrator().migrate(
dbVersion,
@ -166,10 +167,12 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
width: 100,
@ -249,7 +252,7 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
),
suffixIcon: UnconstrainedBox(
child: SizedBox(
height: 70,
height: 40,
child: Row(
children: [
const SizedBox(

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -47,10 +49,12 @@ class _ForgotPasswordDesktopViewState
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
width: 100,

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/debug_info_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/desktop_manage_block_explorers_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/stack_privacy_dialog.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
@ -58,7 +59,7 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
),
TextSpan(
text:
"\n\nConfigurate these settings only if you know what you are doing!",
"\n\nConfigure these settings only if you know what you are doing!",
style: STextStyles.desktopTextExtraExtraSmall(
context),
),
@ -183,7 +184,7 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
PrimaryButton(
label: "Change",
buttonHeight: ButtonHeight.xs,
width: 86,
width: 101,
onPressed: () async {
await showDialog<dynamic>(
context: context,
@ -207,8 +208,6 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
thickness: 0.5,
),
),
/// TODO: Make a dialog popup
Padding(
padding: const EdgeInsets.all(10),
child: Row(
@ -241,6 +240,44 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
],
),
),
const Padding(
padding: EdgeInsets.all(10.0),
child: Divider(
thickness: 0.5,
),
),
Padding(
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Block explorers",
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark),
textAlign: TextAlign.left,
),
PrimaryButton(
buttonHeight: ButtonHeight.xs,
label: "Edit",
width: 101,
onPressed: () async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const DesktopManageBlockExplorersDialog();
},
);
},
),
],
),
),
],
),
),

View file

@ -0,0 +1,266 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/block_explorers.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopManageBlockExplorersDialog extends ConsumerWidget {
const DesktopManageBlockExplorersDialog({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
bool showTestNet = ref.watch(
prefsChangeNotifierProvider.select((value) => value.showTestNetCoins),
);
final List<Coin> coins = showTestNet
? Coin.values
: Coin.values.sublist(0, Coin.values.length - kTestNetCoinCount);
return DesktopDialog(
maxHeight: 850,
maxWidth: 600,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Text(
"Manage block explorers",
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: ListView.separated(
itemCount: coins.length,
separatorBuilder: (_, __) => const SizedBox(
height: 12,
),
itemBuilder: (_, index) {
final coin = coins[index];
return RoundedWhiteContainer(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 14,
),
borderColor: Theme.of(context)
.extension<StackColors>()!
.textSubtitle6,
child: Row(
children: [
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
width: 24,
height: 24,
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"${coin.prettyName} block explorer",
style: STextStyles.desktopTextSmall(context),
),
),
const SizedBox(
width: 12,
),
SvgPicture.asset(
Assets.svg.chevronRight,
width: 20,
height: 20,
),
],
),
onPressed: () {
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return _DesktopEditBlockExplorerDialog(
coin: coin,
);
},
);
},
);
},
),
),
),
],
),
);
}
}
class _DesktopEditBlockExplorerDialog extends ConsumerStatefulWidget {
const _DesktopEditBlockExplorerDialog({Key? key, required this.coin})
: super(key: key);
final Coin coin;
@override
ConsumerState<_DesktopEditBlockExplorerDialog> createState() =>
_DesktopEditBlockExplorerDialogState();
}
class _DesktopEditBlockExplorerDialogState
extends ConsumerState<_DesktopEditBlockExplorerDialog> {
late final TextEditingController _textEditingController;
late final FocusNode _focusNode;
@override
void initState() {
_textEditingController = TextEditingController(
text:
getBlockExplorerTransactionUrlFor(coin: widget.coin, txid: "[TXID]")
.toString()
.replaceAll("%5BTXID%5D", "[TXID]"));
_focusNode = FocusNode();
super.initState();
}
@override
void dispose() {
_textEditingController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxHeight: double.infinity,
maxWidth: 600,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Text(
"${widget.coin.prettyName} block explorer",
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
key: const Key("addCustomNodeNodeAddressFieldKey"),
controller: _textEditingController,
focusNode: _focusNode,
style: STextStyles.field(context),
),
),
const SizedBox(
height: 16,
),
RoundedWhiteContainer(
borderColor:
Theme.of(context).extension<StackColors>()!.textSubtitle6,
child: Text(
"Edit your block explorer above. Keep in mind that"
" every block explorer has a slightly different URL scheme."
"\n\n"
"Paste in your block explorer of choice, then edit in"
" [TXID] where the transaction ID should go, and Stack"
" Wallet will auto fill the transaction ID in that place"
" of the URL.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Save",
buttonHeight: ButtonHeight.l,
onPressed: () async {
_textEditingController.text =
_textEditingController.text.trim();
await setBlockExplorerForCoin(
coin: widget.coin,
url: Uri.parse(
_textEditingController.text,
),
);
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
],
)
],
),
)
],
),
);
}
}

View file

@ -118,44 +118,6 @@ class _AppearanceOptionSettings
],
),
),
// const Padding(
// padding: EdgeInsets.all(10.0),
// child: Divider(
// thickness: 0.5,
// ),
// ),
// Padding(
// padding: const EdgeInsets.all(10.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "System brightness",
// style: STextStyles.desktopTextExtraSmall(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textDark),
// textAlign: TextAlign.left,
// ),
// SizedBox(
// height: 20,
// width: 40,
// child: DraggableSwitchButton(
// isOn: ref.watch(
// prefsChangeNotifierProvider.select(
// (value) => value.enableSystemBrightness),
// ),
// onValueChanged: (newValue) {
// ref
// .read(prefsChangeNotifierProvider)
// .enableSystemBrightness = newValue;
// },
// ),
// )
// ],
// ),
// ),
const Padding(
padding: EdgeInsets.all(10.0),
child: Divider(

View file

@ -6,8 +6,8 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -58,6 +58,8 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
if (Platform.isWindows) {
_coins.remove(Coin.monero);
_coins.remove(Coin.wownero);
} else if (Platform.isLinux) {
_coins.remove(Coin.wownero);
}
searchNodeController = TextEditingController();
@ -250,8 +252,10 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
children: [
Row(
children: [
SvgPicture.asset(
ref.watch(coinIconProvider(coin)),
SvgPicture.file(
File(
ref.watch(coinIconProvider(coin)),
),
width: 24,
height: 24,
),

View file

@ -5,7 +5,6 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart';
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
import 'package:stackwallet/models/buy/response_objects/quote.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
@ -153,6 +152,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/widgets/choose_coin_view.dart';
import 'package:tuple/tuple.dart';
import 'models/isar/models/contact_entry.dart';
class RouteGenerator {
static const bool useMaterialPageRoute = true;

View file

@ -1,107 +1,33 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
class AddressBookService extends ChangeNotifier {
ContactEntry turnContactToEntry({required Contact contact}) {
String? emojiChar = contact.emojiChar;
String name = contact.name;
List<String> addresses = [];
bool isFavorite = contact.isFavorite;
String customId = contact.id;
for (ContactAddressEntry contactAddressEntry in contact.addresses) {
String coin = contactAddressEntry.coin.ticker;
String address = contactAddressEntry.address;
String label = contactAddressEntry.label;
String? other = contactAddressEntry.other;
addresses.add("$coin,$address,$label,$other");
}
return ContactEntry(
emojiChar: emojiChar,
name: name,
addresses: addresses,
isFavorite: isFavorite,
customId: customId,
);
}
Contact turnEntryToContact({required ContactEntry contactEntry}) {
String? emojiChar = contactEntry.emojiChar;
String name = contactEntry.name;
List<ContactAddressEntry> addresses = [];
bool isFavorite = contactEntry.isFavorite;
String id = contactEntry.customId;
for (String addressEntry in contactEntry.addresses) {
List<String> addressEntrySplit = addressEntry.split(",");
Coin coin = coinFromTickerCaseInsensitive(addressEntrySplit[0]);
String address = addressEntrySplit[1];
String label = addressEntrySplit[2];
String? other = addressEntrySplit[3];
addresses.add(ContactAddressEntry(
coin: coin,
address: address,
label: label,
other: other,
));
}
return Contact(
emojiChar: emojiChar,
name: name,
addresses: addresses,
isFavorite: isFavorite,
id: id,
);
}
Contact getContactById(String id) {
ContactEntry getContactById(String id) {
ContactEntry? contactEntry = MainDB.instance.getContactEntry(id: id);
if (contactEntry == null) {
return Contact(
name: "Contact not found",
addresses: [],
isFavorite: false,
);
throw Exception('Contact ID "$id" not found!');
} else {
return turnEntryToContact(contactEntry: contactEntry);
return contactEntry;
}
}
List<Contact> get contacts {
List<ContactEntry> contactEntries = MainDB.instance.getContactEntries();
List<Contact> contactsList = [];
for (ContactEntry contactEntry in contactEntries) {
contactsList.add(turnEntryToContact(contactEntry: contactEntry));
}
return contactsList;
}
List<Contact>? _addressBookEntries;
List<Contact> get addressBookEntries =>
_addressBookEntries ??= _fetchAddressBookEntries();
// Load address book contact entries
List<Contact> _fetchAddressBookEntries() {
return contacts;
}
List<ContactEntry> get contacts => MainDB.instance.getContactEntries();
/// search address book entries
//TODO optimize address book search?
Future<List<Contact>> search(String text) async {
if (text.isEmpty) return addressBookEntries;
var results = (await addressBookEntries).toList();
//TODO search using isar queries
Future<List<ContactEntry>> search(String text) async {
if (text.isEmpty) return contacts;
var results = contacts.toList();
results.retainWhere((contact) => matches(text, contact));
return results;
}
bool matches(String term, Contact contact) {
bool matches(String term, ContactEntry contact) {
if (term.isEmpty) {
return true;
}
@ -125,33 +51,27 @@ class AddressBookService extends ChangeNotifier {
///
/// returns false if it provided [contact]'s id already exists in the database
/// other true if the [contact] was saved
Future<bool> addContact(Contact contact) async {
if (await MainDB.instance.isContactEntryExists(id: contact.id)) {
Future<bool> addContact(ContactEntry contact) async {
if (await MainDB.instance.isContactEntryExists(id: contact.customId)) {
return false;
} else {
await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: contact));
_refreshAddressBookEntries();
await MainDB.instance.putContactEntry(contactEntry: contact);
notifyListeners();
return true;
}
}
/// Edit contact
Future<bool> editContact(Contact editedContact) async {
Future<bool> editContact(ContactEntry editedContact) async {
// over write the contact with edited version
await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: editedContact));
_refreshAddressBookEntries();
await MainDB.instance.putContactEntry(contactEntry: editedContact);
notifyListeners();
return true;
}
/// Remove address book contact entry from db if it exists
Future<void> removeContact(String id) async {
await MainDB.instance.deleteContactEntry(id: id);
_refreshAddressBookEntries();
}
void _refreshAddressBookEntries() {
final newAddressBookEntries = _fetchAddressBookEntries();
_addressBookEntries = newAddressBookEntries;
notifyListeners();
}
}

View file

@ -53,11 +53,11 @@ import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 1;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(294),
fractionDigits: Coin.particl.decimals,
fractionDigits: Coin.bitcoin.decimals,
);
final Amount DUST_LIMIT_P2PKH = Amount(
rawValue: BigInt.from(546),
fractionDigits: Coin.particl.decimals,
fractionDigits: Coin.bitcoin.decimals,
);
const String GENESIS_HASH_MAINNET =
@ -1468,7 +1468,7 @@ class BitcoinWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(key: '${_walletId}_mnemonicPassphrase', value: "");
// Generate and add addresses to relevant arrays

View file

@ -71,7 +71,6 @@ String constructDerivePath({
case 0x80: // bch mainnet wif
switch (derivePathType) {
case DerivePathType.bip44:
case DerivePathType.bip49:
coinType = 145; // bch mainnet
break;
case DerivePathType.bch44: // bitcoin.com wallet specific
@ -95,9 +94,6 @@ String constructDerivePath({
case DerivePathType.bch44:
purpose = 44;
break;
case DerivePathType.bip49:
purpose = 49;
break;
default:
throw Exception("DerivePathType $derivePathType not supported");
}
@ -283,10 +279,6 @@ class BitcoinCashWallet extends CoinServiceAPI
return DerivePathType.bip44;
}
if (decodeBase58[0] == _network.scriptHash) {
// P2SH
return DerivePathType.bip49;
}
throw ArgumentError('Invalid version or Network mismatch');
} else {
try {
@ -419,15 +411,6 @@ class BitcoinCashWallet extends CoinServiceAPI
addrType = isar_models.AddressType.p2pkh;
addressString = bitbox.Address.toCashAddress(addressString);
break;
case DerivePathType.bip49:
addressString = P2SH(
data: PaymentData(
redeem: P2WPKH(data: data, network: _network).data),
network: _network)
.data
.address!;
addrType = isar_models.AddressType.p2sh;
break;
default:
throw Exception("DerivePathType $type not supported");
}
@ -518,7 +501,6 @@ class BitcoinCashWallet extends CoinServiceAPI
final deriveTypes = [
DerivePathType.bip44,
DerivePathType.bip49,
];
if (coin != Coin.bitcoincashTestnet) {
@ -1389,7 +1371,7 @@ class BitcoinCashWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(key: '${_walletId}_mnemonicPassphrase', value: "");
// Generate and add addresses to relevant arrays
@ -1397,10 +1379,6 @@ class BitcoinCashWallet extends CoinServiceAPI
// P2PKH
_generateAddressForChain(0, 0, DerivePathType.bip44),
_generateAddressForChain(1, 0, DerivePathType.bip44),
// P2SH
_generateAddressForChain(0, 0, DerivePathType.bip49),
_generateAddressForChain(1, 0, DerivePathType.bip49),
]);
await db.putAddresses(initialAddresses);
@ -1408,7 +1386,7 @@ class BitcoinCashWallet extends CoinServiceAPI
Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info);
}
/// Generates a new internal or external chain address for the wallet using a BIP44 or BIP49 derivation path.
/// Generates a new internal or external chain address for the wallet using a BIP44 derivation path.
/// [chain] - Use 0 for receiving (external), 1 for change (internal). Should not be any other value!
/// [index] - This can be any integer >= 0
Future<isar_models.Address> _generateAddressForChain(
@ -1449,17 +1427,6 @@ class BitcoinCashWallet extends CoinServiceAPI
addrType = isar_models.AddressType.p2pkh;
address = bitbox.Address.toCashAddress(address);
break;
case DerivePathType.bip49:
address = P2SH(
data: PaymentData(
redeem: P2WPKH(data: data, network: _network).data),
network: _network)
.data
.address!;
addrType = isar_models.AddressType.p2sh;
break;
case DerivePathType.bip84:
throw UnsupportedError("bip84 not supported by BCH");
default:
throw Exception("DerivePathType $derivePathType not supported");
}
@ -1502,13 +1469,6 @@ class BitcoinCashWallet extends CoinServiceAPI
coinType = coin == Coin.bitcoincash ? "0" : "1";
purpose = "44";
break;
case DerivePathType.bip49:
type = isar_models.AddressType.p2sh;
coinType = coin == Coin.bitcoincash ? "145" : "1";
purpose = "49";
break;
case DerivePathType.bip84:
throw UnsupportedError("bip84 not supported by BCH");
default:
throw Exception("DerivePathType $derivePathType not supported");
}
@ -1537,9 +1497,6 @@ class BitcoinCashWallet extends CoinServiceAPI
case DerivePathType.bch44:
key = "${walletId}_${chainId}DerivationsBch44P2PKH";
break;
case DerivePathType.bip49:
key = "${walletId}_${chainId}DerivationsP2SH";
break;
default:
throw UnsupportedError(
"${derivePathType.name} not supported by ${coin.prettyName}");
@ -2721,20 +2678,6 @@ class BitcoinCashWallet extends CoinServiceAPI
redeemScript = null;
break;
case DerivePathType.bip49:
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = p2wpkh.output;
data = P2SH(
data: PaymentData(redeem: p2wpkh),
network: _network,
).data;
break;
default:
throw Exception("DerivePathType unsupported");
}

View file

@ -7,6 +7,7 @@ import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart';
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
@ -243,6 +244,17 @@ abstract class CoinServiceAPI {
cachedClient: cachedClient,
tracker: tracker,
);
case Coin.eCash:
return ECashWallet(
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
);
}
}

View file

@ -1318,7 +1318,7 @@ class DogecoinWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',
value: "",

File diff suppressed because it is too large Load diff

View file

@ -350,7 +350,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
"Attempted to overwrite mnemonic on generate new wallet!");
}
final String mnemonic = bip39.generateMnemonic(strength: 256);
final String mnemonic = bip39.generateMnemonic(strength: 128);
await _secureStore.write(key: '${_walletId}_mnemonic', value: mnemonic);
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',

File diff suppressed because it is too large Load diff

View file

@ -1498,7 +1498,7 @@ class LitecoinWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',
value: "",

View file

@ -12,8 +12,8 @@ import 'package:stackwallet/services/event_bus/events/global/updated_in_backgrou
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/services/mixins/xpubable.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
@ -252,7 +252,8 @@ class Manager with ChangeNotifier {
);
}
bool get hasXPub => _currentWallet is XPubAble;
// TODO: re enable once xpubs have been redone
bool get hasXPub => false; //_currentWallet is XPubAble;
Future<String> get xpub async {
if (!hasXPub) {

View file

@ -1479,7 +1479,7 @@ class NamecoinWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',
value: "",

View file

@ -1394,7 +1394,7 @@ class ParticlWallet extends CoinServiceAPI
}
await _secureStore.write(
key: '${_walletId}_mnemonic',
value: bip39.generateMnemonic(strength: 256));
value: bip39.generateMnemonic(strength: 128));
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',
value: "",

View file

@ -35,9 +35,10 @@ mixin FiroHive {
}
// mintIndex
int? firoGetMintIndex() {
int firoGetMintIndex() {
return DB.instance.get<dynamic>(boxName: _walletId, key: "mintIndex")
as int?;
as int? ??
0;
}
Future<void> firoUpdateMintIndex(int mintIndex) async {

View file

@ -89,7 +89,7 @@ class PriceAPI {
final uri =
Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency"
"=${baseCurrency.toLowerCase()}"
"&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false");

View file

@ -1,38 +1,44 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
final coinIconProvider = Provider.family<String, Coin>((ref, coin) {
final assets = ref.watch(themeProvider).assets;
switch (coin) {
case Coin.bitcoin:
case Coin.bitcoinTestNet:
return assets.bitcoin;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoin;
case Coin.bitcoincash:
case Coin.bitcoincashTestnet:
return assets.bitcoincash;
case Coin.dogecoin:
case Coin.dogecoinTestNet:
return assets.dogecoin;
case Coin.epicCash:
return assets.epicCash;
case Coin.firo:
case Coin.firoTestNet:
return assets.firo;
case Coin.monero:
return assets.monero;
case Coin.wownero:
return assets.wownero;
case Coin.namecoin:
return assets.namecoin;
case Coin.particl:
return assets.particl;
case Coin.ethereum:
return assets.ethereum;
case Coin.nano:
return assets.nano;
final assets = ref.watch(themeAssetsProvider);
if (assets is ThemeAssets) {
switch (coin) {
case Coin.bitcoin:
case Coin.bitcoinTestNet:
return assets.bitcoin;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoin;
case Coin.bitcoincash:
case Coin.bitcoincashTestnet:
return assets.bitcoincash;
case Coin.dogecoin:
case Coin.dogecoinTestNet:
return assets.dogecoin;
case Coin.eCash:
return assets.bitcoin;
case Coin.epicCash:
return assets.epicCash;
case Coin.firo:
case Coin.firoTestNet:
return assets.firo;
case Coin.monero:
return assets.monero;
case Coin.wownero:
return assets.wownero;
case Coin.namecoin:
return assets.namecoin;
case Coin.particl:
return assets.particl;
case Coin.ethereum:
return assets.ethereum;
}
} else {
return (assets as ThemeAssetsV2).coinIcons[coin.mainNetVersion]!;
}
});

View file

@ -1,77 +1,92 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
final coinImageProvider = Provider.family<String, Coin>((ref, coin) {
final assets = ref.watch(themeProvider).assets;
switch (coin) {
case Coin.bitcoin:
return assets.bitcoinImage;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoinImage;
case Coin.bitcoincash:
return assets.bitcoincashImage;
case Coin.dogecoin:
return assets.dogecoinImage;
case Coin.epicCash:
return assets.epicCashImage;
case Coin.firo:
return assets.firoImage;
case Coin.monero:
return assets.moneroImage;
case Coin.wownero:
return assets.wowneroImage;
case Coin.namecoin:
return assets.namecoinImage;
case Coin.particl:
return assets.particlImage;
case Coin.bitcoinTestNet:
return assets.bitcoinImage;
case Coin.bitcoincashTestnet:
return assets.bitcoincashImage;
case Coin.firoTestNet:
return assets.firoImage;
case Coin.dogecoinTestNet:
return assets.dogecoinImage;
case Coin.ethereum:
return assets.ethereumImage;
case Coin.nano:
return assets.nano;
final assets = ref.watch(themeAssetsProvider);
if (assets is ThemeAssets) {
switch (coin) {
case Coin.bitcoin:
return assets.bitcoinImage;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoinImage;
case Coin.bitcoincash:
return assets.bitcoincashImage;
case Coin.dogecoin:
return assets.dogecoinImage;
case Coin.eCash:
return assets.bitcoinImage;
case Coin.epicCash:
return assets.epicCashImage;
case Coin.firo:
return assets.firoImage;
case Coin.monero:
return assets.moneroImage;
case Coin.wownero:
return assets.wowneroImage;
case Coin.namecoin:
return assets.namecoinImage;
case Coin.particl:
return assets.particlImage;
case Coin.bitcoinTestNet:
return assets.bitcoinImage;
case Coin.bitcoincashTestnet:
return assets.bitcoincashImage;
case Coin.firoTestNet:
return assets.firoImage;
case Coin.dogecoinTestNet:
return assets.dogecoinImage;
case Coin.ethereum:
return assets.ethereumImage;
}
} else {
return (assets as ThemeAssetsV2).coinImages[coin.mainNetVersion]!;
}
});
final coinImageSecondaryProvider = Provider.family<String, Coin>((ref, coin) {
final assets = ref.watch(themeProvider).assets;
switch (coin) {
case Coin.bitcoin:
case Coin.bitcoinTestNet:
return assets.bitcoinImageSecondary;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoinImageSecondary;
case Coin.bitcoincash:
case Coin.bitcoincashTestnet:
return assets.bitcoincashImageSecondary;
case Coin.dogecoin:
case Coin.dogecoinTestNet:
return assets.dogecoinImageSecondary;
case Coin.epicCash:
return assets.epicCashImageSecondary;
case Coin.firo:
case Coin.firoTestNet:
return assets.firoImageSecondary;
case Coin.monero:
return assets.moneroImageSecondary;
case Coin.wownero:
return assets.wowneroImageSecondary;
case Coin.namecoin:
return assets.namecoinImageSecondary;
case Coin.particl:
return assets.particlImageSecondary;
case Coin.ethereum:
return assets.ethereumImageSecondary;
case Coin.nano:
return assets.nano;
final assets = ref.watch(themeAssetsProvider);
if (assets is ThemeAssets) {
switch (coin) {
case Coin.bitcoin:
return assets.bitcoinImageSecondary;
case Coin.litecoin:
case Coin.litecoinTestNet:
return assets.litecoinImageSecondary;
case Coin.bitcoincash:
return assets.bitcoincashImageSecondary;
case Coin.dogecoin:
return assets.dogecoinImageSecondary;
case Coin.eCash:
return assets.bitcoinImageSecondary;
case Coin.epicCash:
return assets.epicCashImageSecondary;
case Coin.firo:
return assets.firoImageSecondary;
case Coin.monero:
return assets.moneroImageSecondary;
case Coin.wownero:
return assets.wowneroImageSecondary;
case Coin.namecoin:
return assets.namecoinImageSecondary;
case Coin.particl:
return assets.particlImageSecondary;
case Coin.bitcoinTestNet:
return assets.bitcoinImageSecondary;
case Coin.bitcoincashTestnet:
return assets.bitcoincashImageSecondary;
case Coin.firoTestNet:
return assets.firoImageSecondary;
case Coin.dogecoinTestNet:
return assets.dogecoinImageSecondary;
case Coin.ethereum:
return assets.ethereumImageSecondary;
}
} else {
return (assets as ThemeAssetsV2).coinSecondaryImages[coin.mainNetVersion]!;
}
});

View file

@ -12,6 +12,7 @@ class CoinThemeColorDefault {
Color get firo => const Color(0xFFFF897A);
Color get dogecoin => const Color(0xFFFFE079);
Color get epicCash => const Color(0xFFC5C7CB);
Color get eCash => const Color(0xFFC5C7CB);
Color get ethereum => const Color(0xFFA7ADE9);
Color get monero => const Color(0xFFFF9E6B);
Color get namecoin => const Color(0xFF91B1E1);
@ -33,6 +34,8 @@ class CoinThemeColorDefault {
case Coin.dogecoin:
case Coin.dogecoinTestNet:
return dogecoin;
case Coin.eCash:
return eCash;
case Coin.epicCash:
return epicCash;
case Coin.ethereum:

View file

@ -103,6 +103,7 @@ class StackColors extends ThemeExtension<StackColors> {
// icons
final Color bottomNavIconBack;
final Color bottomNavIconIcon;
final Color bottomNavIconIconHighlighted;
final Color topNavIconPrimary;
final Color topNavIconGreen;
final Color topNavIconYellow;
@ -281,6 +282,7 @@ class StackColors extends ThemeExtension<StackColors> {
required this.snackBarTextInfo,
required this.bottomNavIconBack,
required this.bottomNavIconIcon,
required this.bottomNavIconIconHighlighted,
required this.topNavIconPrimary,
required this.topNavIconGreen,
required this.topNavIconYellow,
@ -441,6 +443,7 @@ class StackColors extends ThemeExtension<StackColors> {
snackBarTextInfo: colorTheme.snackBarTextInfo,
bottomNavIconBack: colorTheme.bottomNavIconBack,
bottomNavIconIcon: colorTheme.bottomNavIconIcon,
bottomNavIconIconHighlighted: colorTheme.bottomNavIconIconHighlighted,
topNavIconPrimary: colorTheme.topNavIconPrimary,
topNavIconGreen: colorTheme.topNavIconGreen,
topNavIconYellow: colorTheme.topNavIconYellow,
@ -603,6 +606,7 @@ class StackColors extends ThemeExtension<StackColors> {
Color? snackBarTextInfo,
Color? bottomNavIconBack,
Color? bottomNavIconIcon,
Color? bottomNavIconIconHighlighted,
Color? topNavIconPrimary,
Color? topNavIconGreen,
Color? topNavIconYellow,
@ -776,6 +780,8 @@ class StackColors extends ThemeExtension<StackColors> {
snackBarTextInfo: snackBarTextInfo ?? this.snackBarTextInfo,
bottomNavIconBack: bottomNavIconBack ?? this.bottomNavIconBack,
bottomNavIconIcon: bottomNavIconIcon ?? this.bottomNavIconIcon,
bottomNavIconIconHighlighted:
bottomNavIconIconHighlighted ?? this.bottomNavIconIconHighlighted,
topNavIconPrimary: topNavIconPrimary ?? this.topNavIconPrimary,
topNavIconGreen: topNavIconGreen ?? this.topNavIconGreen,
topNavIconYellow: topNavIconYellow ?? this.topNavIconYellow,
@ -1279,6 +1285,11 @@ class StackColors extends ThemeExtension<StackColors> {
other.bottomNavIconIcon,
t,
)!,
bottomNavIconIconHighlighted: Color.lerp(
bottomNavIconIconHighlighted,
other.bottomNavIconIconHighlighted,
t,
)!,
topNavIconPrimary: Color.lerp(
topNavIconPrimary,
other.topNavIconPrimary,
@ -1671,6 +1682,8 @@ class StackColors extends ThemeExtension<StackColors> {
return _coin.dogecoin;
case Coin.epicCash:
return _coin.epicCash;
case Coin.eCash:
return _coin.eCash;
case Coin.ethereum:
return _coin.ethereum;
case Coin.firo:

View file

@ -20,3 +20,11 @@ final themeProvider = StateProvider<StackTheme>(
),
),
);
final themeAssetsProvider = StateProvider<IThemeAssets>(
(ref) => ref.watch(
themeProvider.select(
(value) => value.assets,
),
),
);

View file

@ -1,9 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:archive/archive_io.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart';
import 'package:isar/isar.dart';
@ -17,6 +17,7 @@ final pThemeService = Provider<ThemeService>((ref) {
});
class ThemeService {
static const _currentDefaultThemeVersion = 2;
ThemeService._();
static ThemeService? _instance;
static ThemeService get instance => _instance ??= ThemeService._();
@ -92,6 +93,70 @@ class ThemeService {
}
}
Future<void> checkDefaultThemesOnStartup() async {
// install default themes
if (!(await ThemeService.instance.verifyInstalled(themeId: "light"))) {
Logging.instance.log(
"Installing default light theme...",
level: LogLevel.Info,
);
final lightZip = await rootBundle.load("assets/default_themes/light.zip");
await ThemeService.instance
.install(themeArchiveData: lightZip.buffer.asUint8List());
Logging.instance.log(
"Installing default light theme... finished",
level: LogLevel.Info,
);
} else {
// check installed version
final theme = ThemeService.instance.getTheme(themeId: "light");
if ((theme?.version ?? 1) < _currentDefaultThemeVersion) {
Logging.instance.log(
"Updating default light theme...",
level: LogLevel.Info,
);
final lightZip =
await rootBundle.load("assets/default_themes/light.zip");
await ThemeService.instance
.install(themeArchiveData: lightZip.buffer.asUint8List());
Logging.instance.log(
"Updating default light theme... finished",
level: LogLevel.Info,
);
}
}
if (!(await ThemeService.instance.verifyInstalled(themeId: "dark"))) {
Logging.instance.log(
"Installing default dark theme... ",
level: LogLevel.Info,
);
final darkZip = await rootBundle.load("assets/default_themes/dark.zip");
await ThemeService.instance
.install(themeArchiveData: darkZip.buffer.asUint8List());
Logging.instance.log(
"Installing default dark theme... finished",
level: LogLevel.Info,
);
} else {
// check installed version
final theme = ThemeService.instance.getTheme(themeId: "dark");
if ((theme?.version ?? 1) < _currentDefaultThemeVersion) {
Logging.instance.log(
"Updating default dark theme...",
level: LogLevel.Info,
);
final darkZip = await rootBundle.load("assets/default_themes/dark.zip");
await ThemeService.instance
.install(themeArchiveData: darkZip.buffer.asUint8List());
Logging.instance.log(
"Updating default dark theme... finished",
level: LogLevel.Info,
);
}
}
}
// TODO more thorough check/verification of theme
Future<bool> verifyInstalled({required String themeId}) async {
final dbHasTheme =
@ -175,6 +240,7 @@ class ThemeService {
class StackThemeMetaData {
final String name;
final String id;
final int version;
final String sha256;
final String size;
final String previewImageUrl;
@ -182,6 +248,7 @@ class StackThemeMetaData {
StackThemeMetaData({
required this.name,
required this.id,
required this.version,
required this.sha256,
required this.size,
required this.previewImageUrl,
@ -192,6 +259,7 @@ class StackThemeMetaData {
return StackThemeMetaData(
name: map["name"] as String,
id: map["id"] as String,
version: map["version"] as int? ?? 1,
sha256: map["sha256"] as String,
size: map["size"] as String,
previewImageUrl: map["previewImageUrl"] as String,
@ -210,6 +278,7 @@ class StackThemeMetaData {
return "$runtimeType("
"name: $name, "
"id: $id, "
"version: $version, "
"sha256: $sha256, "
"size: $size, "
"previewImageUrl: $previewImageUrl"

View file

@ -6,6 +6,7 @@ import 'package:flutter_libepiccash/epic_cash.dart';
import 'package:nanodart/nanodart.dart';
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/litecoin/litecoin_wallet.dart';
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
@ -62,6 +63,8 @@ class AddressUtils {
return true; //TODO - validate ETH address
case Coin.firo:
return Address.validateAddress(address, firoNetwork);
case Coin.eCash:
return Address.validateAddress(address, eCashNetwork);
case Coin.monero:
return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) ||
RegExp("[a-zA-Z0-9]{106}").hasMatch(address);

View file

@ -165,6 +165,7 @@ class _SVG {
String get questionMessage => "assets/svg/message-question.svg";
String get envelope => "assets/svg/envelope.svg";
String get share => "assets/svg/share-2.svg";
String get recycle => "assets/svg/anonymize.svg";
String get anonymize => "assets/svg/tx-icon-anonymize.svg";
String get anonymizePending => "assets/svg/tx-icon-anonymize-pending.svg";
String get anonymizeFailed => "assets/svg/tx-icon-anonymize-failed.svg";
@ -193,7 +194,7 @@ class _SVG {
String get exchange3 => "assets/svg/exchange-3.svg";
String get messageQuestion => "assets/svg/message-question-1.svg";
String get list => "assets/svg/list-ul.svg";
String get unclaimedPaynym => "assets/svg/unclaimed.png";
String get unclaimedPaynym => "assets/svg/unclaimed.svg";
String get trocadorRatingA => "assets/svg/trocador_rating_a.svg";
String get trocadorRatingB => "assets/svg/trocador_rating_b.svg";

View file

@ -17,6 +17,8 @@ Uri getDefaultBlockExplorerUrlFor({
return Uri.parse("https://mempool.space/testnet/tx/$txid");
case Coin.dogecoin:
return Uri.parse("https://chain.so/tx/DOGE/$txid");
case Coin.eCash:
return Uri.parse("https://explorer.bitcoinabc.org/tx/$txid");
case Coin.dogecoinTestNet:
return Uri.parse("https://chain.so/tx/DOGETEST/$txid");
case Coin.epicCash:

View file

@ -25,6 +25,7 @@ abstract class Constants {
// static bool enableBuy = enableExchange;
// // true; // true for development,
static const BigInt _satsPerCoinECash = BigInt.from(100);
static final BigInt _satsPerCoinEthereum = BigInt.from(1000000000000000000);
static final BigInt _satsPerCoinMonero = BigInt.from(1000000000000);
static final BigInt _satsPerCoinWownero = BigInt.from(100000000000);
@ -35,6 +36,7 @@ abstract class Constants {
static const int _decimalPlacesWownero = 11;
static const int _decimalPlacesMonero = 12;
static const int _decimalPlacesEthereum = 18;
static const int _decimalPlacesECash = 2;
static const int notificationsMax = 0xFFFFFFFF;
static const Duration networkAliveTimerDuration = Duration(seconds: 10);
@ -44,7 +46,7 @@ abstract class Constants {
// Enable Logger.print statements
static const bool disableLogger = false;
static const int currentHiveDbVersion = 8;
static const int currentDataVersion = 10;
static const int rescanV1 = 1;
@ -76,6 +78,9 @@ abstract class Constants {
case Coin.ethereum:
return _satsPerCoinEthereum;
case Coin.eCash:
return _satsPerCoinECash;
}
}
@ -107,6 +112,9 @@ abstract class Constants {
case Coin.ethereum:
return _decimalPlacesEthereum;
case Coin.eCash:
return _decimalPlacesECash;
}
}
@ -123,6 +131,7 @@ abstract class Constants {
case Coin.bitcoinTestNet:
case Coin.dogecoinTestNet:
case Coin.firoTestNet:
case Coin.eCash:
case Coin.epicCash:
case Coin.ethereum:
case Coin.namecoin:
@ -146,10 +155,9 @@ abstract class Constants {
switch (coin) {
case Coin.bitcoin:
case Coin.bitcoinTestNet:
return 600;
case Coin.bitcoincash:
case Coin.bitcoincashTestnet:
case Coin.eCash:
return 600;
case Coin.dogecoin:
@ -187,7 +195,7 @@ abstract class Constants {
}
}
static const int seedPhraseWordCountBip39 = 24;
static const int seedPhraseWordCountBip39 = 12;
static const int seedPhraseWordCountMonero = 25;
static const Map<int, String> monthMapShort = {

View file

@ -3,9 +3,12 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart'
as isar_contact;
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
import 'package:stackwallet/models/models.dart';
import 'package:stackwallet/models/node_model.dart';
@ -290,6 +293,48 @@ class DbVersionMigrator with WalletDB {
// try to continue migrating
return await migrate(8, secureStore: secureStore);
case 8:
// migrate
await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
final walletsService =
WalletsService(secureStorageInterface: secureStore);
final walletInfoList = await walletsService.walletNames;
await MainDB.instance.initMainDB();
for (final walletId in walletInfoList.keys) {
final info = walletInfoList[walletId]!;
if (info.coin == Coin.bitcoincash ||
info.coin == Coin.bitcoincashTestnet) {
final ids = await MainDB.instance
.getAddresses(walletId)
.filter()
.typeEqualTo(isar_models.AddressType.p2sh)
.idProperty()
.findAll();
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.addresses.deleteAll(ids);
});
}
}
// update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 9);
// try to continue migrating
return await migrate(9, secureStore: secureStore);
case 9:
// migrate
await _v9();
// update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 10);
// try to continue migrating
return await migrate(10, secureStore: secureStore);
default:
// finally return
return;
@ -447,4 +492,50 @@ class DbVersionMigrator with WalletDB {
}
}
}
Future<void> _v9() async {
final addressBookBox = await Hive.openBox<dynamic>(DB.boxNameAddressBook);
await MainDB.instance.initMainDB();
final keys = List<String>.from(addressBookBox.keys);
final contacts = keys
.map((id) => Contact.fromJson(
Map<String, dynamic>.from(
addressBookBox.get(id) as Map,
),
))
.toList(growable: false);
final List<isar_contact.ContactEntry> newContacts = [];
for (final contact in contacts) {
final List<isar_contact.ContactAddressEntry> newContactAddressEntries =
[];
for (final entry in contact.addresses) {
newContactAddressEntries.add(
isar_contact.ContactAddressEntry()
..coinName = entry.coin.name
..address = entry.address
..label = entry.label
..other = entry.other,
);
}
final newContact = isar_contact.ContactEntry(
name: contact.name,
addresses: newContactAddressEntries,
isFavorite: contact.isFavorite,
customId: contact.id,
);
newContacts.add(newContact);
}
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.contactEntrys.putAll(newContacts);
});
await addressBookBox.deleteFromDisk();
}
}

View file

@ -1,6 +1,5 @@
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
// import 'package:web3dart/browser.dart';
abstract class DefaultNodes {
static const String defaultNodeIdPrefix = "default_";
@ -13,6 +12,7 @@ abstract class DefaultNodes {
dogecoin,
firo,
monero,
eCash,
epicCash,
ethereum,
bitcoincash,
@ -231,6 +231,18 @@ abstract class DefaultNodes {
isDown: false,
);
static NodeModel get eCash => NodeModel(
host: "electrum.bitcoinabc.org",
port: 50002,
name: defaultName,
id: _nodeId(Coin.eCash),
useSSL: true,
enabled: true,
coinName: Coin.eCash.name,
isFailover: true,
isDown: false,
);
static NodeModel getNodeFor(Coin coin) {
switch (coin) {
case Coin.bitcoin:
@ -245,6 +257,9 @@ abstract class DefaultNodes {
case Coin.dogecoin:
return dogecoin;
case Coin.eCash:
return eCash;
case Coin.epicCash:
return epicCash;

View file

@ -3,6 +3,7 @@ import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart'
as bch;
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart'
as doge;
import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart' as ecash;
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart'
as epic;
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart'
@ -23,6 +24,7 @@ enum Coin {
bitcoin,
bitcoincash,
dogecoin,
eCash,
epicCash,
ethereum,
firo,
@ -61,6 +63,8 @@ extension CoinExt on Coin {
return "Dogecoin";
case Coin.epicCash:
return "Epic Cash";
case Coin.eCash:
return "E-Cash";
case Coin.ethereum:
return "Ethereum";
case Coin.firo:
@ -102,6 +106,8 @@ extension CoinExt on Coin {
return "EPIC";
case Coin.ethereum:
return "ETH";
case Coin.eCash:
return "XEC";
case Coin.firo:
return "FIRO";
case Coin.monero:
@ -142,6 +148,8 @@ extension CoinExt on Coin {
return "epic";
case Coin.ethereum:
return "ethereum";
case Coin.eCash:
return "ecash";
case Coin.firo:
return "firo";
case Coin.monero:
@ -181,6 +189,7 @@ extension CoinExt on Coin {
case Coin.bitcoincashTestnet:
case Coin.firoTestNet:
case Coin.dogecoinTestNet:
case Coin.eCash:
return true;
case Coin.epicCash:
@ -204,6 +213,7 @@ extension CoinExt on Coin {
case Coin.firo:
case Coin.namecoin:
case Coin.particl:
case Coin.eCash:
case Coin.epicCash:
case Coin.monero:
case Coin.wownero:
@ -231,6 +241,7 @@ extension CoinExt on Coin {
case Coin.monero:
case Coin.wownero:
case Coin.nano:
case Coin.eCash:
return false;
case Coin.dogecoinTestNet:
@ -256,6 +267,7 @@ extension CoinExt on Coin {
case Coin.monero:
case Coin.wownero:
case Coin.nano:
case Coin.eCash:
return this;
case Coin.dogecoinTestNet:
@ -300,6 +312,9 @@ extension CoinExt on Coin {
case Coin.epicCash:
return epic.MINIMUM_CONFIRMATIONS;
case Coin.eCash:
return ecash.MINIMUM_CONFIRMATIONS;
case Coin.ethereum:
return eth.MINIMUM_CONFIRMATIONS;
@ -354,6 +369,11 @@ Coin coinFromPrettyName(String name) {
case "firo":
return Coin.firo;
case "E-Cash":
case "ecash":
case "eCash":
return Coin.eCash;
case "Monero":
case "monero":
return Coin.monero;
@ -423,6 +443,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
return Coin.dogecoin;
case "epic":
return Coin.epicCash;
case "xec":
return Coin.eCash;
case "eth":
return Coin.ethereum;
case "firo":

View file

@ -6,6 +6,7 @@ enum DerivePathType {
bip49,
bip84,
eth,
eCash44,
}
extension DerivePathTypeExt on DerivePathType {
@ -27,6 +28,9 @@ extension DerivePathTypeExt on DerivePathType {
case Coin.particl:
return DerivePathType.bip84;
case Coin.eCash:
return DerivePathType.eCash44;
case Coin.ethereum: // TODO: do we need something here?
return DerivePathType.eth;

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
@ -50,4 +52,15 @@ abstract class Util {
}
return MaterialColor(color.value, swatch);
}
static void printJson(dynamic json) {
if (json is Map || json is List) {
final spaces = ' ' * 4;
final encoder = JsonEncoder.withIndent(spaces);
final pretty = encoder.convert(json);
log(pretty);
} else {
log(dynamic.toString());
}
}
}

View file

@ -1,10 +1,13 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -14,8 +17,6 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import '../themes/theme_providers.dart';
class AddressBookCard extends ConsumerStatefulWidget {
const AddressBookCard({
Key? key,
@ -48,7 +49,7 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
@override
Widget build(BuildContext context) {
// provider hack to prevent trying to update widget with deleted contact
Contact? _contact;
ContactEntry? _contact;
try {
_contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(contactId)));
@ -81,7 +82,7 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
width: 32,
height: 32,
decoration: BoxDecoration(
color: contact.id == "default"
color: contact.customId == "default"
? Theme.of(context)
.extension<StackColors>()!
.myStackContactIconBG
@ -90,12 +91,14 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(32),
),
child: contact.id == "default"
child: contact.customId == "default"
? Center(
child: SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
child: SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
width: 20,
@ -176,7 +179,7 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
useSafeArea: true,
barrierDismissible: true,
builder: (_) => ContactPopUp(
contactId: contact.id,
contactId: contact.customId,
),
);
},

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -47,10 +49,12 @@ class _LivingStackIconState extends ConsumerState<LivingStackIcon> {
child: AnimatedScale(
duration: const Duration(milliseconds: 200),
scale: _hovering ? 1.2 : 1,
child: SvgPicture.asset(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
child: SvgPicture.file(
File(
ref.watch(
themeProvider.select(
(value) => value.assets.stackIcon,
),
),
),
),

View file

@ -46,6 +46,12 @@ class RoundedContainer extends StatelessWidget {
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * radiusMultiplier,
),
side: borderColor == null
? BorderSide.none
: BorderSide(
color: borderColor!,
width: 1.2,
),
),
onPressed: onPressed,
child: child,

View file

@ -24,7 +24,7 @@ class TradeCard extends ConsumerWidget {
final Trade trade;
final VoidCallback onTap;
String _fetchIconAssetForStatus(String statusString, ThemeAssets assets) {
String _fetchIconAssetForStatus(String statusString, IThemeAssets assets) {
ChangeNowTransactionStatus? status;
try {
if (statusString.toLowerCase().startsWith("waiting")) {
@ -89,11 +89,7 @@ class TradeCard extends ConsumerWidget {
File(
_fetchIconAssetForStatus(
trade.status,
ref.watch(
themeProvider.select(
(value) => value.assets,
),
),
ref.watch(themeAssetsProvider),
),
),
width: 32,

View file

@ -14,7 +14,7 @@ class ExchangeNavIcon extends ConsumerWidget {
File(
ref.watch(
themeProvider.select(
(value) => value.assets.buy,
(value) => value.assets.exchange,
),
),
),

View file

@ -68,9 +68,10 @@ class WalletNavigationBarItem extends ConsumerWidget {
Text(
data.label ?? "",
style: STextStyles.buttonSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavText),
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavText,
),
),
],
),
@ -112,7 +113,11 @@ class WalletNavigationBarMoreItem extends ConsumerWidget {
child: Text(
data.label ?? "",
textAlign: TextAlign.center,
style: STextStyles.buttonSmall(context),
style: STextStyles.buttonSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavText,
),
),
),
const SizedBox(

Some files were not shown because too many files have changed in this diff Show more