Skip to content

Commit e661d36

Browse files
author
Egor Komarov
committed
feat(EWM-517): nft (wip)
1 parent b89f538 commit e661d36

39 files changed

+2272
-144
lines changed

assets/translations/en.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,11 +691,23 @@
691691
"nftWord": "NFT",
692692
"nftEmptyListTitle": "You don't have any NFTs yet",
693693
"nftEmptyListSubtitle": "Visit our marketplace for Everscale collections or import NFTs manually",
694+
"nftEmptyListSubtitleNoMarket": "You can add the NFTs manually",
694695
"visitMarketplace": "Visit marketplace",
695696
"addNFT": "Add NFT",
696697
"nftMyCollections": "My collections",
698+
"nftMyNfts": "My NFTs",
697699
"nftPasteHint": "Paste NFT address",
698700
"invalidAddressFormat": "Invalid address format",
699701
"nftOwnerError": "Not the NFT owner",
700-
"nftImportHint": "This address is available in the transaction details of your NFT purchase."
702+
"nftImportHint": "This address is available in the transaction details of your NFT purchase.",
703+
"nftViewInExplorer": "View details in Explorer",
704+
"nftHideCollection": "Hide collection",
705+
"nftCollectionHidden": "The collection is hidden",
706+
"showMore": "Show more",
707+
"owner": "Owner",
708+
"manager": "Manager",
709+
"balanceWord": "Balance",
710+
"transfer": "Transfer",
711+
"transferTokens": "Transfer tokens",
712+
"openInMarketplace": "Open in marketplace"
701713
}

assets/translations/ko.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,11 +691,23 @@
691691
"nftWord": "NFT",
692692
"nftEmptyListTitle": "You don't have any NFTs yet",
693693
"nftEmptyListSubtitle": "Visit our marketplace for Everscale collections or import NFTs manually",
694+
"nftEmptyListSubtitleNoMarket": "You can add the NFTs manually",
694695
"visitMarketplace": "Visit marketplace",
695696
"addNFT": "Add NFT",
696697
"nftMyCollections": "My collections",
698+
"nftMyNfts": "My NFTs",
697699
"nftPasteHint": "Paste NFT address",
698700
"invalidAddressFormat": "Invalid address format",
699701
"nftOwnerError": "Not the NFT owner",
700-
"nftImportHint": "This address is available in the transaction details of your NFT purchase."
702+
"nftImportHint": "This address is available in the transaction details of your NFT purchase.",
703+
"nftViewInExplorer": "View details in Explorer",
704+
"nftHideCollection": "Hide collection",
705+
"nftCollectionHidden": "The collection is hidden",
706+
"showMore": "Show more",
707+
"owner": "Owner",
708+
"manager": "Manager",
709+
"balanceWord": "Balance",
710+
"transfer": "Transfer",
711+
"transferTokens": "Transfer tokens",
712+
"openInMarketplace": "Open in marketplace"
701713
}

lib/di/di.config.dart

Lines changed: 26 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/feature/messenger/messenger.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'data/message.dart';
2+
export 'domain/service/messenger_service.dart';
3+
export 'widget/toast_message.dart';

lib/feature/nft/domain/nft_service.dart

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:app/app/service/service.dart';
22
import 'package:app/feature/nft/nft.dart';
33
import 'package:collection/collection.dart';
44
import 'package:injectable/injectable.dart';
5+
import 'package:logging/logging.dart';
56
import 'package:nekoton_repository/nekoton_repository.dart';
67
import 'package:rxdart/rxdart.dart';
78

@@ -13,17 +14,25 @@ class NftService {
1314
this._appStorageService,
1415
);
1516

17+
static final _logger = Logger('NftService');
18+
1619
final NekotonRepository _nekotonRepository;
1720
final NftStorageService _nftStorageService;
1821
final AppStorageService _appStorageService;
1922

2023
final Map<Address, NftCollection> _collections = {};
2124

22-
Stream<NftDisplayMode?> get displayModeStream => _appStorageService
23-
.getValueStream<NftDisplayMode>(StorageKey.nftGridMode());
25+
Stream<NftDisplayMode?> get displayModeStream =>
26+
_appStorageService.getValueStream<String>(StorageKey.nftGridMode()).map(
27+
(name) => switch (name) {
28+
'grid' => NftDisplayMode.grid,
29+
'list' => NftDisplayMode.list,
30+
_ => null,
31+
},
32+
);
2433

2534
void setDisplayMode(NftDisplayMode mode) {
26-
_appStorageService.addValue(StorageKey.nftGridMode(), mode);
35+
_appStorageService.addValue(StorageKey.nftGridMode(), mode.name);
2736
}
2837

2938
Stream<List<CollectionMeta>> getAccountCollectionsMetaStream(Address owner) {
@@ -51,6 +60,50 @@ class NftService {
5160
);
5261
}
5362

63+
Stream<NftTransferEvent> getNftTransferEventStream({
64+
required Address owner,
65+
required Address collection,
66+
}) {
67+
return _nekotonRepository.nftTransferEventStream.where(
68+
(event) =>
69+
event.collection == collection &&
70+
(event.sender == owner || event.recipient == owner),
71+
);
72+
}
73+
74+
Future<NftCollection?> getCollection(Address address) async {
75+
if (_collections.containsKey(address)) {
76+
return _collections[address];
77+
}
78+
79+
try {
80+
final collection = await _nekotonRepository.getNftCollection(address);
81+
_collections[address] = collection;
82+
return collection;
83+
} catch (e) {
84+
_logger.warning('Failed to get NFT collection for address $address: $e');
85+
return null;
86+
}
87+
}
88+
89+
Future<NftItem?> getNftItem({
90+
required Address address,
91+
required Address owner,
92+
}) async {
93+
try {
94+
final item = await _nekotonRepository.getNftItem(
95+
address: address,
96+
owner: owner,
97+
);
98+
return item;
99+
} catch (e) {
100+
_logger.warning(
101+
'Failed to get NFT item for address $address (owner: $owner): $e',
102+
);
103+
return null;
104+
}
105+
}
106+
54107
Future<void> scanNftCollections(Address owner) async {
55108
final networkGroup = _nekotonRepository.currentTransport.networkGroup;
56109
// TODO: whitelist collections
@@ -88,6 +141,19 @@ class NftService {
88141
);
89142
}
90143

144+
void addCollection({
145+
required Address account,
146+
required Address collection,
147+
}) {
148+
_nftStorageService.addMetadata(
149+
account: account,
150+
collectionMeta: CollectionMeta(
151+
collection: collection,
152+
networkGroup: _nekotonRepository.currentTransport.networkGroup,
153+
),
154+
);
155+
}
156+
91157
void hideCollection({
92158
required Address account,
93159
required Address collection,

lib/feature/nft/nft.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export 'data/data.dart';
22
export 'domain/domain.dart';
33
export 'route.dart';
44
export 'view/view.dart';
5+
export 'widgets/widgets.dart';

lib/feature/nft/route.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:injectable/injectable.dart';
77
class NftRoute extends CompassRouteParameterless<NftRouteData> {
88
NftRoute(
99
@Named.from(AddNftRoute) CompassBaseRoute addNftRoute,
10+
@Named.from(NftCollectionRoute) CompassBaseRoute nftCollectionRoute,
1011
) : super(
1112
name: 'nft',
1213
path: '/nft',
@@ -16,6 +17,7 @@ class NftRoute extends CompassRouteParameterless<NftRouteData> {
1617
builder: (context, _, __) => const NftPageWidget(),
1718
compassBaseRoutes: [
1819
addNftRoute,
20+
nftCollectionRoute,
1921
],
2022
);
2123

lib/feature/nft/view/add_nft/add_nft_widget.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class AddNftWidget extends ElementaryWidget<AddNftWidgetModel> {
2929
return Padding(
3030
padding: const EdgeInsets.symmetric(horizontal: DimensSizeV2.d16),
3131
child: Column(
32+
crossAxisAlignment: CrossAxisAlignment.start,
3233
children: [
3334
if (account != null) AccountInfo(account: account),
3435
const SizedBox(height: DimensSizeV2.d12),
@@ -53,15 +54,15 @@ class AddNftWidget extends ElementaryWidget<AddNftWidgetModel> {
5354
if (error == null || error.isEmpty) {
5455
return Text(
5556
LocaleKeys.nftImportHint.tr(),
56-
style: theme.textStyles.labelSmall.copyWith(
57+
style: theme.textStyles.labelXSmall.copyWith(
5758
color: theme.colors.content1,
5859
),
5960
);
6061
}
6162

6263
return Text(
6364
error,
64-
style: theme.textStyles.labelSmall.copyWith(
65+
style: theme.textStyles.labelXSmall.copyWith(
6566
color: theme.colors.negative,
6667
),
6768
);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export 'nft_collection_page_model.dart';
2+
export 'nft_collection_page_widget.dart';
3+
export 'nft_collection_page_wm.dart';
4+
export 'route.dart';
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import 'package:app/app/service/service.dart';
2+
import 'package:app/feature/messenger/messenger.dart';
3+
import 'package:app/feature/nft/nft.dart';
4+
import 'package:app/generated/generated.dart';
5+
import 'package:elementary/elementary.dart';
6+
import 'package:nekoton_repository/nekoton_repository.dart' hide Message;
7+
import 'package:rxdart/rxdart.dart';
8+
9+
const _limit = 8;
10+
11+
class NftCollectionPageModel extends ElementaryModel {
12+
NftCollectionPageModel(
13+
ErrorHandler errorHandler,
14+
this._nftService,
15+
this._currentAccountsService,
16+
this._nekotonRepository,
17+
this._messengerService,
18+
) : super(errorHandler: errorHandler);
19+
20+
final NftService _nftService;
21+
final CurrentAccountsService _currentAccountsService;
22+
final NekotonRepository _nekotonRepository;
23+
final MessengerService _messengerService;
24+
25+
Stream<KeyAccount?> get currentAccountStream =>
26+
_currentAccountsService.currentActiveAccountStream;
27+
28+
Stream<NftDisplayMode?> get displayModeStream =>
29+
_nftService.displayModeStream;
30+
31+
Future<Address> get _owner =>
32+
currentAccountStream.mapNotNull((e) => e?.address).first;
33+
34+
Stream<NftTransferEvent> getNftTransferEventStream(Address collection) =>
35+
_owner.asStream().switchMap(
36+
(owner) => _nftService.getNftTransferEventStream(
37+
owner: owner,
38+
collection: collection,
39+
),
40+
);
41+
42+
Future<NftCollection?> getCollection(Address address) =>
43+
_nftService.getCollection(address);
44+
45+
Future<NftList> getNtfList({
46+
required Address collection,
47+
String? continuation,
48+
}) async =>
49+
_nekotonRepository.getNtfList(
50+
owner: await _owner,
51+
collection: collection,
52+
limit: _limit,
53+
continuation: continuation,
54+
);
55+
56+
void setDisplayMode(NftDisplayMode mode) => _nftService.setDisplayMode(mode);
57+
58+
Future<void> hideCollection(Address collection) async {
59+
_nftService.hideCollection(
60+
account: await _owner,
61+
collection: collection,
62+
);
63+
64+
_messengerService.show(
65+
Message.successful(message: LocaleKeys.nftCollectionHidden.tr()),
66+
);
67+
}
68+
69+
String getAccountExplorerLink(Address address) =>
70+
_nekotonRepository.currentTransport.accountExplorerLink(address);
71+
}

0 commit comments

Comments
 (0)