Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
clients
Ğecko
Commits
a4f5b44a
Commit
a4f5b44a
authored
Jun 16, 2022
by
poka
Browse files
Hello Activity screen; paged history works for all profiles
parent
1c4da17d
Pipeline
#16172
waiting for manual action with stages
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
config/indexer_endpoints.json
View file @
a4f5b44a
[
"https://duniter-indexer.coinduf.eu
/v1/graphql
"
,
"http://192.168.1.72:8080
/v1/graphql
"
"https://duniter-indexer.coinduf.eu"
,
"http://192.168.1.72:8080"
]
lib/main.dart
View file @
a4f5b44a
...
...
@@ -58,6 +58,7 @@ Future<void> main() async {
HomeProvider
_homeProvider
=
HomeProvider
();
DuniterIndexer
_duniterIndexer
=
DuniterIndexer
();
await
initHiveForFlutter
();
await
_homeProvider
.
initHive
();
appVersion
=
await
_homeProvider
.
getAppVersion
();
prefs
=
await
SharedPreferences
.
getInstance
();
...
...
@@ -67,7 +68,6 @@ Future<void> main() async {
Hive
.
registerAdapter
(
ChestDataAdapter
());
Hive
.
registerAdapter
(
G1WalletsListAdapter
());
Hive
.
registerAdapter
(
IdAdapter
());
// Hive.registerAdapter(KeyStoreDataAdapter());
walletBox
=
await
Hive
.
openBox
<
WalletData
>(
"walletBox"
);
chestBox
=
await
Hive
.
openBox
<
ChestData
>(
"chestBox"
);
...
...
@@ -82,10 +82,7 @@ Future<void> main() async {
}
// log.d(await configBox.get('endpoint'));
await
_duniterIndexer
.
getValidIndexerEndpoint
();
// _duniterIndexer.indexerEndpoint = "http://192.168.1.72:8080/v1/graphql";
// _duniterIndexer.indexerEndpoint =
// "https://duniter-indexer.coinduf.eu/v1/graphql";
_duniterIndexer
.
getValidIndexerEndpoint
();
HttpOverrides
.
global
=
MyHttpOverrides
();
...
...
@@ -131,20 +128,11 @@ class Gecko extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
SystemChrome
.
setPreferredOrientations
([
DeviceOrientation
.
portraitUp
]);
final
_httpLink
=
HttpLink
(
indexerEndpoint
!,
);
final
_client
=
ValueNotifier
(
GraphQLClient
(
cache:
GraphQLCache
(),
link:
_httpLink
,
),
);
// To configure multi_endpoints GraphQLProvider: https://stackoverflow.com/q/70656513/8301867
return
MultiProvider
(
providers:
[
// Provider(create: (context) => HistoryProvider()),
ChangeNotifierProvider
(
create:
(
_
)
=>
HomeProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
WalletsProfilesProvider
(
''
)),
ChangeNotifierProvider
(
create:
(
_
)
=>
MyWalletsProvider
()),
...
...
@@ -156,44 +144,41 @@ class Gecko extends StatelessWidget {
ChangeNotifierProvider
(
create:
(
_
)
=>
SubstrateSdk
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
DuniterIndexer
())
],
child:
GraphQLProvider
(
client:
_client
,
child:
MaterialApp
(
builder:
(
context
,
widget
)
=>
ResponsiveWrapper
.
builder
(
BouncingScrollWrapper
.
builder
(
context
,
widget
!),
maxWidth:
1200
,
minWidth:
480
,
defaultScale:
true
,
breakpoints:
[
const
ResponsiveBreakpoint
.
resize
(
480
,
name:
MOBILE
),
const
ResponsiveBreakpoint
.
autoScale
(
800
,
name:
TABLET
),
const
ResponsiveBreakpoint
.
resize
(
1000
,
name:
DESKTOP
),
],
background:
Container
(
color:
backgroundColor
)),
title:
'Ğecko'
,
theme:
ThemeData
(
appBarTheme:
const
AppBarTheme
(
color:
Color
(
0xffFFD58D
),
foregroundColor:
Color
(
0xFF000000
),
),
primaryColor:
const
Color
(
0xffFFD58D
),
textTheme:
const
TextTheme
(
bodyText1:
TextStyle
(
fontSize:
16
),
bodyText2:
TextStyle
(
fontSize:
18
),
).
apply
(
bodyColor:
const
Color
(
0xFF000000
),
),
colorScheme:
ColorScheme
.
fromSwatch
().
copyWith
(
secondary:
Colors
.
grey
[
850
]),
child:
MaterialApp
(
builder:
(
context
,
widget
)
=>
ResponsiveWrapper
.
builder
(
BouncingScrollWrapper
.
builder
(
context
,
widget
!),
maxWidth:
1200
,
minWidth:
480
,
defaultScale:
true
,
breakpoints:
[
const
ResponsiveBreakpoint
.
resize
(
480
,
name:
MOBILE
),
const
ResponsiveBreakpoint
.
autoScale
(
800
,
name:
TABLET
),
const
ResponsiveBreakpoint
.
resize
(
1000
,
name:
DESKTOP
),
],
background:
Container
(
color:
backgroundColor
)),
title:
'Ğecko'
,
theme:
ThemeData
(
appBarTheme:
const
AppBarTheme
(
color:
Color
(
0xffFFD58D
),
foregroundColor:
Color
(
0xFF000000
),
),
primaryColor:
const
Color
(
0xffFFD58D
),
textTheme:
const
TextTheme
(
bodyText1:
TextStyle
(
fontSize:
16
),
bodyText2:
TextStyle
(
fontSize:
18
),
).
apply
(
bodyColor:
const
Color
(
0xFF000000
),
),
home:
const
HomeScreen
(),
initialRoute:
"/"
,
routes:
{
'/mywallets'
:
(
context
)
=>
const
WalletsHome
(),
'/search'
:
(
context
)
=>
const
SearchScreen
(),
'/searchResult'
:
(
context
)
=>
const
SearchResultScreen
(),
},
colorScheme:
ColorScheme
.
fromSwatch
().
copyWith
(
secondary:
Colors
.
grey
[
850
]),
),
home:
const
HomeScreen
(),
initialRoute:
"/"
,
routes:
{
'/mywallets'
:
(
context
)
=>
const
WalletsHome
(),
'/search'
:
(
context
)
=>
const
SearchScreen
(),
'/searchResult'
:
(
context
)
=>
const
SearchResultScreen
(),
},
),
);
}
...
...
lib/models/queries_indexer.dart
View file @
a4f5b44a
...
...
@@ -52,19 +52,31 @@ query ($address: String!) {
'''
;
const
String
getHistoryByAddressQ3
=
r''
'
query (
$address
: String!) {
query (
$address
: String!
,
$number
: Int!,
$cursor
: String
) {
transaction_connection(where:
{_or: [
{issuer_id: {_eq:
$address
}},
{receiver_id: {_eq:
$address
}}
]},
order_by: {created_at: desc}) {
order_by: {created_at: desc},
first:
$number
,
after:
$cursor
) {
edges {
node {
amount
created_at
issuer_id
receiver_id
issuer {
identity {
name
}
}
receiver {
identity {
name
}
}
}
}
pageInfo {
...
...
lib/providers/duniter_indexer.dart
View file @
a4f5b44a
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:gecko/globals.dart'
;
...
...
@@ -17,7 +16,11 @@ import 'package:provider/provider.dart';
class
DuniterIndexer
with
ChangeNotifier
{
Map
<
String
,
String
?>
walletNameIndexer
=
{};
String
?
fetchMoreCursor
;
Map
?
pageInfo
;
int
nPage
=
1
;
int
nRepositories
=
20
;
List
?
transBC
;
void
reload
()
{
notifyListeners
();
...
...
@@ -30,7 +33,8 @@ class DuniterIndexer with ChangeNotifier {
final
_client
=
HttpClient
();
_client
.
connectionTimeout
=
const
Duration
(
milliseconds:
1000
);
try
{
final
request
=
await
_client
.
postUrl
(
Uri
.
parse
(
oldEndpoint
));
final
request
=
await
_client
.
postUrl
(
Uri
.
parse
(
'
$oldEndpoint
/v1/graphql'
));
final
response
=
await
request
.
close
();
if
(
response
.
statusCode
!=
200
)
{
log
.
d
(
'INDEXER IS OFFILINE'
);
...
...
@@ -74,7 +78,8 @@ class DuniterIndexer with ChangeNotifier {
}
try
{
final
request
=
await
_client
.
postUrl
(
Uri
.
parse
(
_listEndpoints
[
i
]));
final
request
=
await
_client
.
postUrl
(
Uri
.
parse
(
'
${_listEndpoints[i]}
/v1/graphql'
));
final
response
=
await
request
.
close
();
indexerEndpoint
=
_listEndpoints
[
i
];
...
...
@@ -122,54 +127,67 @@ class DuniterIndexer with ChangeNotifier {
}
}
}
final
_httpLink
=
HttpLink
(
'
$indexerEndpoint
/v1/graphql'
,
);
final
_client
=
ValueNotifier
(
GraphQLClient
(
cache:
GraphQLCache
(
store:
HiveStore
()),
link:
_httpLink
,
),
);
return
GraphQLProvider
(
client:
_client
,
child:
Query
(
options:
QueryOptions
(
document:
gql
(
getNameByAddressQ
),
// this is the query string you just created
variables:
{
'address'
:
address
,
},
// pollInterval: const Duration(seconds: 10),
),
builder:
(
QueryResult
result
,
{
VoidCallback
?
refetch
,
FetchMore
?
fetchMore
})
{
if
(
result
.
hasException
)
{
return
Text
(
result
.
exception
.
toString
());
}
if
(
result
.
isLoading
)
{
return
const
Text
(
'Loading'
);
}
return
Query
(
options:
QueryOptions
(
document:
gql
(
getNameByAddressQ
),
// this is the query string you just created
variables:
{
'address'
:
address
,
},
// pollInterval: const Duration(seconds: 10),
),
builder:
(
QueryResult
result
,
{
VoidCallback
?
refetch
,
FetchMore
?
fetchMore
})
{
if
(
result
.
hasException
)
{
return
Text
(
result
.
exception
.
toString
());
}
if
(
result
.
isLoading
)
{
return
const
Text
(
'Loading'
);
}
walletNameIndexer
[
address
]
=
result
.
data
?[
'account_by_pk'
]?[
'identity'
]?[
'name'
];
if
(
walletNameIndexer
[
address
]
==
null
)
{
if
(
wallet
==
null
)
{
return
const
SizedBox
();
}
else
{
if
(
canEdit
)
{
return
_walletOptions
.
walletName
(
context
,
wallet
,
size
,
_color
);
walletNameIndexer
[
address
]
=
result
.
data
?[
'account_by_pk'
]?[
'identity'
]?[
'name'
];
if
(
walletNameIndexer
[
address
]
==
null
)
{
if
(
wallet
==
null
)
{
return
const
SizedBox
();
}
else
{
return
_walletOptions
.
walletNameController
(
context
,
wallet
,
size
);
if
(
canEdit
)
{
return
_walletOptions
.
walletName
(
context
,
wallet
,
size
,
_color
);
}
else
{
return
_walletOptions
.
walletNameController
(
context
,
wallet
,
size
);
}
}
}
}
return
Text
(
_color
==
Colors
.
grey
[
700
]!
?
'(
${
walletNameIndexer[address]!
}
)'
:
walletNameIndexer
[
address
]!,
style:
TextStyle
(
fontSize:
size
,
color:
_color
,
font
Weight:
fontWeight
,
fontStyle:
fontStyle
,
)
,
);
}
);
return
Text
(
_color
==
Colors
.
grey
[
700
]!
?
'(
${walletNameIndexer[address]!}
)'
:
walletNameIndexer
[
address
]!
,
style:
TextStyle
(
fontSize:
size
,
color:
_color
,
fontWeight:
fontWeight
,
font
Style:
fontStyle
,
)
,
)
;
}),
);
}
Widget
searchIdentity
(
BuildContext
context
,
String
name
)
{
...
...
@@ -183,85 +201,184 @@ class DuniterIndexer with ChangeNotifier {
return
const
Text
(
'Aucun résultat'
);
}
return
Query
(
options:
QueryOptions
(
document:
gql
(
searchAddressByNameQ
),
// this is the query string you just created
variables:
{
'name'
:
name
,
},
// pollInterval: const Duration(seconds: 10),
),
builder:
(
QueryResult
result
,
{
VoidCallback
?
refetch
,
FetchMore
?
fetchMore
})
{
if
(
result
.
hasException
)
{
return
Text
(
result
.
exception
.
toString
());
}
if
(
result
.
isLoading
)
{
return
const
Text
(
'Loading'
);
}
final
List
identities
=
result
.
data
?[
'search_identity'
]
??
[];
if
(
identities
.
isEmpty
)
{
return
const
Text
(
'Aucun résultat'
);
}
int
keyID
=
0
;
double
_avatarSize
=
55
;
return
Expanded
(
child:
ListView
(
children:
<
Widget
>[
for
(
Map
profile
in
identities
)
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
5
),
child:
ListTile
(
key:
Key
(
'searchResult
${keyID++}
'
),
horizontalTitleGap:
40
,
contentPadding:
const
EdgeInsets
.
all
(
5
),
leading:
_cesiumPlusProvider
.
defaultAvatar
(
_avatarSize
),
title:
Row
(
children:
<
Widget
>[
Text
(
getShortPubkey
(
profile
[
'id'
]),
style:
const
TextStyle
(
fontSize:
18
,
fontFamily:
'Monospace'
,
fontWeight:
FontWeight
.
w500
),
textAlign:
TextAlign
.
center
),
]),
trailing:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
balance
(
context
,
profile
[
'id'
],
16
)]),
subtitle:
Row
(
children:
<
Widget
>[
Text
(
profile
[
'name'
]
??
''
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w500
),
textAlign:
TextAlign
.
center
),
]),
dense:
false
,
isThreeLine:
false
,
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
{
_walletsProfiles
.
address
=
profile
[
'id'
];
return
WalletViewScreen
(
pubkey:
profile
[
'id'
],
username:
g1WalletsBox
.
get
(
profile
[
'id'
])?.
id
?.
username
,
avatar:
g1WalletsBox
.
get
(
profile
[
'id'
])?.
avatar
,
);
}),
);
}),
),
]),
);
});
final
_httpLink
=
HttpLink
(
'
$indexerEndpoint
/v1/graphql'
,
);
final
_client
=
ValueNotifier
(
GraphQLClient
(
cache:
GraphQLCache
(
store:
HiveStore
()),
link:
_httpLink
,
),
);
return
GraphQLProvider
(
client:
_client
,
child:
Query
(
options:
QueryOptions
(
document:
gql
(
searchAddressByNameQ
),
// this is the query string you just created
variables:
{
'name'
:
name
,
},
// pollInterval: const Duration(seconds: 10),
),
builder:
(
QueryResult
result
,
{
VoidCallback
?
refetch
,
FetchMore
?
fetchMore
})
{
if
(
result
.
hasException
)
{
return
Text
(
result
.
exception
.
toString
());
}
if
(
result
.
isLoading
)
{
return
const
Text
(
'Loading'
);
}
final
List
identities
=
result
.
data
?[
'search_identity'
]
??
[];
if
(
identities
.
isEmpty
)
{
return
const
Text
(
'Aucun résultat'
);
}
int
keyID
=
0
;
double
_avatarSize
=
55
;
return
Expanded
(
child:
ListView
(
children:
<
Widget
>[
for
(
Map
profile
in
identities
)
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
5
),
child:
ListTile
(
key:
Key
(
'searchResult
${keyID++}
'
),
horizontalTitleGap:
40
,
contentPadding:
const
EdgeInsets
.
all
(
5
),
leading:
_cesiumPlusProvider
.
defaultAvatar
(
_avatarSize
),
title:
Row
(
children:
<
Widget
>[
Text
(
getShortPubkey
(
profile
[
'id'
]),
style:
const
TextStyle
(
fontSize:
18
,
fontFamily:
'Monospace'
,
fontWeight:
FontWeight
.
w500
),
textAlign:
TextAlign
.
center
),
]),
trailing:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
balance
(
context
,
profile
[
'id'
],
16
)]),
subtitle:
Row
(
children:
<
Widget
>[
Text
(
profile
[
'name'
]
??
''
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w500
),
textAlign:
TextAlign
.
center
),
]),
dense:
false
,
isThreeLine:
false
,
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
{
_walletsProfiles
.
address
=
profile
[
'id'
];
return
WalletViewScreen
(
pubkey:
profile
[
'id'
],
username:
g1WalletsBox
.
get
(
profile
[
'id'
])
?.
id
?.
username
,
avatar:
g1WalletsBox
.
get
(
profile
[
'id'
])?.
avatar
,
);
}),
);
}),
),
]),
);
}),
);
}
List
parseHistory
(
blockchainTX
,
_pubkey
)
{
var
transBC
=
[];
int
i
=
0
;
checkHistoryResult
(
QueryResult
<
Object
?>
result
,
FetchMoreOptions
options
,
String
address
)
{
for
(
final
trans
in
blockchainTX
)
{
final
transaction
=
trans
[
'node'
];
final
direction
=
transaction
[
'issuer_id'
]
!=
_pubkey
?
'RECEIVED'
:
'SENT'
;
}
transBC
.
add
(
i
);
transBC
[
i
]
=
[];
transBC
[
i
].
add
(
DateTime
.
parse
(
transaction
[
'created_at'
]));
final
int
amountBrut
=
transaction
[
'amount'
];
final
num
amount
=
removeDecimalZero
(
amountBrut
/
100
);
if
(
direction
==
"RECEIVED"
)
{
transBC
[
i
].
add
(
transaction
[
'issuer_id'
]);
transBC
[
i
].
add
(
transaction
[
'issuer'
][
'identity'
]?[
'name'
]
??
''
);
transBC
[
i
].
add
(
amount
.
toString
());
}
else
if
(
direction
==
"SENT"
)
{
transBC
[
i
].
add
(
transaction
[
'receiver_id'
]);
transBC
[
i
].
add
(
transaction
[
'receiver'
][
'identity'
]?[
'name'
]
??
''
);
transBC
[
i
].
add
(
'- '
+
amount
.
toString
());
}
// transBC[i].add(''); //transaction comment
i
++;
}
return
transBC
;
}
FetchMoreOptions
?
checkQueryResult
(
result
,
opts
,
_pubkey
)
{
final
List
<
dynamic
>?
blockchainTX
=
(
result
.
data
[
'transaction_connection'
][
'edges'
]
as
List
<
dynamic
>?);
// final List<dynamic> mempoolTX =
// (result.data['txsHistoryMp']['receiving'] as List<dynamic>);
pageInfo
=
result
.
data
[
'transaction_connection'
][
'pageInfo'
];
fetchMoreCursor
=
pageInfo
![
'endCursor'
];
if
(
fetchMoreCursor
==
null
)
nPage
=
1
;
log
.
d
(
fetchMoreCursor
);
if
(
nPage
==
1
)
{
nRepositories
=
40
;
}
else
if
(
nPage
==
2
)
{
nRepositories
=
100
;
}
// nRepositories = 10;
nPage
++;
if
(
fetchMoreCursor
!=
null
)
{
opts
=
FetchMoreOptions
(
variables:
{
'cursor'
:
fetchMoreCursor
,
'number'
:
nRepositories
},
updateQuery:
(
previousResultData
,
fetchMoreResultData
)
{
final
List
<
dynamic
>
repos
=
[
...
previousResultData
![
'transaction_connection'
][
'edges'
]
as
List
<
dynamic
>,
...
fetchMoreResultData
![
'transaction_connection'
][
'edges'
]
as
List
<
dynamic
>
];
log
.
d
(
'repos: '
+
previousResultData
.
toString
());
log
.
d
(
'repos: '
+
fetchMoreResultData
.
toString
());
log
.
d
(
'repos: '
+
repos
.
toString
());
fetchMoreResultData
[
'transaction_connection'
][
'edges'
]
=
repos
;
return
fetchMoreResultData
;
},
);
}
log
.
d
(
"###### DEBUG H Parse blockchainTX list. Cursor:
$fetchMoreCursor
######"
);
if
(
fetchMoreCursor
!=
null
)
{
transBC
=
parseHistory
(
blockchainTX
,
_pubkey
);
}
else
{
log
.
i
(
"###### DEBUG H - Début de l'historique"
);
}
return
opts
;
}
num
removeDecimalZero
(
double
n
)
{
String
result
=
n
.
toStringAsFixed
(
n
.
truncateToDouble
()
==
n
?
0
:
2
);
return
num
.
parse
(
result
);
}
// checkHistoryResult(
// QueryResult<Object?> result, FetchMoreOptions options, String address) {}
}
lib/providers/wallet_options.dart
View file @
a4f5b44a
...
...
@@ -577,7 +577,7 @@ Widget getCerts(BuildContext context, String address, double size,
return
FutureBuilder
(
future:
_sdk
.
getCerts
(
address
),
builder:
(
BuildContext
context
,
AsyncSnapshot
<
List
<
int
>>
_certs
)
{
log
.
d
(
_certs
.
data
);
//
log.d(_certs.data);
return
_certs
.
data
?[
0
]
!=
0
&&
_certs
.
data
!=
null
?
Row
(
...
...
lib/providers/wallets_profiles.dart
View file @
a4f5b44a
import
'dart:io'
;
import
'package:flutter/material.dart'
;
import
'package:gecko/globals.dart'
;
import
'package:gecko/providers/substrate_sdk.dart'
;
import
'package:gecko/screens/wallet_view.dart'
;
import
'package:graphql_flutter/graphql_flutter.dart'
;
import
'package:jdenticon_dart/jdenticon_dart.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
// import 'package:qrscan/qrscan.dart' as scanner;
import
'package:barcode_scan2/barcode_scan2.dart'
;
import
'dart:math'
;
import
'package:intl/intl.dart'
;
class
WalletsProfilesProvider
with
ChangeNotifier
{
WalletsProfilesProvider
(
this
.
address
);
String
?
address
=
''
;
String
pubkeyShort
=
''
;