Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dcf7f12
Refactor: Adapted My Transactions page/widgets structure for All Tran…
muknta Nov 18, 2024
1526a12
Feature: Implemented transactions page with transaction details drawer
muknta Nov 27, 2024
ef1766f
Feature: Implemented blocks, block details pages with API
dpajak99 Jan 2, 2024
fb3b63e
Related to #22: Introduce new versioning. Rely on major+minor instead…
muknta Dec 13, 2025
d1fd821
Closes #30: Replace stale InterxHeaders with cached InterxStatus request
muknta Dec 13, 2025
54e152b
Closes #21: Adapt Broadcasts to new Interx API
muknta Dec 13, 2025
7170959
Closes #18: Transactions list with drawer with new Intex support
muknta Dec 13, 2025
c15df60
Closes #19: Blocks list with drawer with new Intex support
muknta Dec 13, 2025
82a93bc
Related to #29: Add temporary 30 secs caching for dynamic sliver lists
muknta Dec 13, 2025
758f937
Bugfix: Align the request & response expectations of aliases and othe…
muknta Dec 13, 2025
83d29bd
Closes #20: Adapt Dashboard requests to new Interx. Combine it from n…
muknta Dec 13, 2025
04010da
Bugfix: Disable non-functional date range in block list; Missing quer…
muknta Dec 17, 2025
9f705ce
Refactor: Fix tests, adapt for new Interx structure. Skip 23 of them …
ux-dev-kira Feb 2, 2026
1276e0c
Closes #35: Support "has_txs" param at blocks request & add filtering…
ux-dev-kira Feb 18, 2026
dc67134
Refactor: Fix all previously skipped tests by actualizing mocked data
ux-dev-kira Feb 19, 2026
9ca1b43
Closes #25: Bugfix: Cors proxy is not applied to custom http on IPFS …
ux-dev-kira Feb 20, 2026
e9ca06e
Bump app version
ux-dev-kira Feb 20, 2026
943b8d3
Feature: Make Kira logo clickable and navigating to Dashboard
ux-dev-kira Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .fvm/fvm_config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"flutterSdkVersion": "3.16.9",
"flavors": {}
"flutterSdkVersion": "3.16.9"
}
25 changes: 25 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "miro",
"request": "launch",
"type": "dart"
},
{
"name": "miro (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "miro (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ analyzer:
avoid_void_async: error
cascade_invocations: error
deprecated_consistency: error
flutter_style_todos: error
# flutter_style_todos: error # ignore for now
leading_newlines_in_multiline_strings: error
literal_only_boolean_expressions: error
missing_whitespace_between_adjacent_strings: error
Expand Down
6 changes: 1 addition & 5 deletions assets/network_list_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
"network_list": [
{
"name": "Public Node 1",
"address": "http://148.251.69.56:11000"
},
{
"name": "Public Node 2",
"address": "http://128.140.42.2:11000"
"address": "http://3.123.154.245:11000"
}
]
}
59 changes: 45 additions & 14 deletions lib/blocs/generic/network_module/network_module_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'package:miro/shared/controllers/browser/rpc_browser_url_controller.dart'
import 'package:miro/shared/models/network/data/connection_status_type.dart';
import 'package:miro/shared/models/network/status/a_network_status_model.dart';
import 'package:miro/shared/models/network/status/network_empty_model.dart';
import 'package:miro/shared/models/network/status/network_offline_model.dart';
import 'package:miro/shared/models/network/status/network_unknown_model.dart';
import 'package:miro/shared/models/network/status/online/a_network_online_model.dart';
import 'package:miro/shared/models/tokens/token_default_denom_model.dart';
Expand All @@ -35,6 +36,10 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {
late Timer _timer;
TokenDefaultDenomModel tokenDefaultDenomModel = TokenDefaultDenomModel.empty();

static const List<String> _ignoredNetworks = <String>[
// No unused Public Nodes at `network_list_config.json`, nothing to ignore yet
];

NetworkModuleBloc() : super(NetworkModuleState.disconnected()) {
on<NetworkModuleInitEvent>(_mapInitEventToState);
on<NetworkModuleRefreshEvent>(_mapRefreshEventToState);
Expand All @@ -49,24 +54,25 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {
await super.close();
}

Future<void> _mapInitEventToState(NetworkModuleInitEvent networkModuleInitEvent, Emitter<NetworkModuleState> emit) async {
NetworkUnknownModel defaultNetworkUnknownModel = await _appConfig.getDefaultNetworkUnknownModel();
void _mapInitEventToState(NetworkModuleInitEvent networkModuleInitEvent, Emitter<NetworkModuleState> emit) {
NetworkUnknownModel defaultNetworkUnknownModel = _appConfig.getDefaultNetworkUnknownModel();

add(NetworkModuleAutoConnectEvent(defaultNetworkUnknownModel));
_updateNetworkStatusModelList(ignoreNetworkUnknownModel: defaultNetworkUnknownModel);

_timer = Timer.periodic(_appConfig.refreshInterval, (Timer timer) {
// TODO(dominik): Debug info. Should be removed before release
// ignore: avoid_print
print('Refreshing Network: ${timer.tick}');
add(NetworkModuleRefreshEvent());
});
}

Future<void> _mapRefreshEventToState(NetworkModuleRefreshEvent networkModuleRefreshEvent, Emitter<NetworkModuleState> emit) async {
Future<void> _mapRefreshEventToState(
NetworkModuleRefreshEvent networkModuleRefreshEvent, Emitter<NetworkModuleState> emit) async {
if (state.networkStatusModel is NetworkEmptyModel || state.isRefreshing) {
_updateNetworkStatusModelList();
} else {
if (_ignoredNetworks.contains(state.networkStatusModel.uri.host)) {
return;
}
emit(NetworkModuleState.refreshing(state.networkStatusModel));
NetworkUnknownModel networkUnknownModel = NetworkUnknownModel.fromNetworkStatusModel(state.networkStatusModel);
ANetworkStatusModel networkStatusModel = await _networkModuleService.getNetworkStatusModel(networkUnknownModel);
Expand All @@ -76,7 +82,7 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {

bool networkUnchangedBool = networkStatusModel.uri == state.networkStatusModel.uri;
if (networkUnchangedBool) {
await _networkCustomSectionCubit.updateNetworks(networkStatusModel);
_networkCustomSectionCubit.updateNetworks(networkStatusModel);
emit(NetworkModuleState.connected(networkStatusModel));
_refreshTokenDefaultDenomModel(networkStatusModel);
await _checkIfSignOutNeeded();
Expand All @@ -86,32 +92,47 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {
await _networkCustomSectionCubit.refreshNetworks();
}

Future<void> _mapAutoConnectEventToState(NetworkModuleAutoConnectEvent networkModuleAutoConnectEvent, Emitter<NetworkModuleState> emit) async {
Future<void> _mapAutoConnectEventToState(
NetworkModuleAutoConnectEvent networkModuleAutoConnectEvent, Emitter<NetworkModuleState> emit) async {
NetworkUnknownModel networkUnknownModel = networkModuleAutoConnectEvent.networkUnknownModel;
emit(NetworkModuleState.connecting(networkUnknownModel));

if (initializationCompleter.isCompleted == false) {
initializationCompleter.complete();
}

if (_ignoredNetworks.contains(networkUnknownModel.uri.host)) {
ANetworkStatusModel networkStatusModel = NetworkOfflineModel(
connectionStatusType: ConnectionStatusType.disconnected,
uri: networkUnknownModel.uri,
lastRefreshDateTime: DateTime.now(),
name: networkUnknownModel.name,
);
_networkListCubit.setNetworkStatusModel(networkStatusModel: networkStatusModel);
_networkCustomSectionCubit.updateNetworks(networkStatusModel);
return;
}
emit(NetworkModuleState.connecting(networkUnknownModel));

ANetworkStatusModel networkStatusModel = await _networkModuleService.getNetworkStatusModel(networkUnknownModel);
_networkListCubit.setNetworkStatusModel(networkStatusModel: networkStatusModel);

bool networkUnchangedBool = NetworkUtils.compareUrisByUrn(networkStatusModel.uri, state.networkStatusModel.uri);

if (networkUnchangedBool) {
_rpcBrowserUrlController.setRpcAddress(networkStatusModel);
await _networkCustomSectionCubit.updateNetworks(networkStatusModel);
_networkCustomSectionCubit.updateNetworks(networkStatusModel);
emit(NetworkModuleState.connected(networkStatusModel));
_refreshTokenDefaultDenomModel(networkStatusModel);
} else {
await _networkCustomSectionCubit.updateNetworks();
_networkCustomSectionCubit.updateNetworks();
}
}

Future<void> _mapConnectEventToState(NetworkModuleConnectEvent networkModuleConnectEvent, Emitter<NetworkModuleState> emit) async {
Future<void> _mapConnectEventToState(
NetworkModuleConnectEvent networkModuleConnectEvent, Emitter<NetworkModuleState> emit) async {
ANetworkOnlineModel networkOnlineModel = networkModuleConnectEvent.networkOnlineModel;
_rpcBrowserUrlController.setRpcAddress(networkOnlineModel);
await _networkCustomSectionCubit.updateNetworks(networkOnlineModel);
_networkCustomSectionCubit.updateNetworks(networkOnlineModel);
emit(NetworkModuleState.connected(networkOnlineModel));
_switchTokenDefaultDenomModel(networkOnlineModel);
await _checkIfSignOutNeeded();
Expand All @@ -122,7 +143,7 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {
Emitter<NetworkModuleState> emit,
) async {
_rpcBrowserUrlController.removeRpcAddress();
await _networkCustomSectionCubit.updateNetworks(null);
_networkCustomSectionCubit.updateNetworks(null);
emit(NetworkModuleState.disconnected());
}

Expand Down Expand Up @@ -159,6 +180,16 @@ class NetworkModuleBloc extends Bloc<ANetworkModuleEvent, NetworkModuleState> {
}

Future<void> _updateNetworkStatusModel({required NetworkUnknownModel networkUnknownModel}) async {
if (_ignoredNetworks.contains(networkUnknownModel.uri.host)) {
ANetworkStatusModel networkStatusModel = NetworkOfflineModel(
connectionStatusType: ConnectionStatusType.disconnected,
uri: networkUnknownModel.uri,
lastRefreshDateTime: DateTime.now(),
name: networkUnknownModel.name,
);
_networkListCubit.setNetworkStatusModel(networkStatusModel: networkStatusModel);
return;
}
ANetworkStatusModel networkStatusModel = await _networkModuleService.getNetworkStatusModel(networkUnknownModel);
_networkListCubit.setNetworkStatusModel(networkStatusModel: networkStatusModel);
}
Expand Down
10 changes: 10 additions & 0 deletions lib/blocs/pages/blocks/blocks_page/blocks_page_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'blocks_page_state.dart';

class BlocksPageCubit extends Cubit<BlocksPageState> {
BlocksPageCubit() : super(const BlocksPageState(isAgeFormatBool: true));

void switchDateFormat() => emit(BlocksPageState(isAgeFormatBool: !state.isAgeFormatBool));
}
10 changes: 10 additions & 0 deletions lib/blocs/pages/blocks/blocks_page/blocks_page_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
part of 'blocks_page_cubit.dart';

class BlocksPageState extends Equatable {
final bool isAgeFormatBool;

const BlocksPageState({required this.isAgeFormatBool});

@override
List<Object?> get props => <Object?>[isAgeFormatBool];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'transactions_page_state.dart';

class TransactionsPageCubit extends Cubit<TransactionsPageState> {
TransactionsPageCubit() : super(const TransactionsPageState(isAgeFormatBool: true));

void switchDateFormat() => emit(TransactionsPageState(isAgeFormatBool: !state.isAgeFormatBool));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
part of 'transactions_page_cubit.dart';

class TransactionsPageState extends Equatable {
final bool isAgeFormatBool;

const TransactionsPageState({required this.isAgeFormatBool});

@override
List<Object?> get props => <Object?>[isAgeFormatBool];
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import 'package:miro/blocs/pages/transactions/tx_broadcast/a_tx_broadcast_state.dart';
import 'package:miro/shared/models/transactions/broadcast_resp_model.dart';
import 'package:miro/infra/dto/api_kira/broadcast/response/broadcast_resp.dart';

class TxBroadcastCompletedState extends ATxBroadcastState {
final BroadcastRespModel broadcastRespModel;
final BroadcastResp broadcastResp;

const TxBroadcastCompletedState({
required this.broadcastRespModel,
required this.broadcastResp,
});

@override
List<Object?> get props => <Object>[broadcastRespModel];
List<Object?> get props => <Object>[broadcastResp];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import 'package:miro/blocs/pages/transactions/tx_broadcast/states/tx_broadcast_c
import 'package:miro/blocs/pages/transactions/tx_broadcast/states/tx_broadcast_error_state.dart';
import 'package:miro/blocs/pages/transactions/tx_broadcast/states/tx_broadcast_loading_state.dart';
import 'package:miro/config/locator.dart';
import 'package:miro/infra/dto/api_kira/broadcast/response/broadcast_resp.dart';
import 'package:miro/infra/exceptions/dio_connect_exception.dart';
import 'package:miro/infra/exceptions/dio_parse_exception.dart';
import 'package:miro/infra/exceptions/tx_broadcast_exception.dart';
import 'package:miro/infra/services/api_kira/broadcast_service.dart';
import 'package:miro/shared/models/network/error_explorer_model.dart';
import 'package:miro/shared/models/transactions/broadcast_resp_model.dart';
import 'package:miro/shared/models/transactions/signed_transaction_model.dart';

class TxBroadcastCubit extends Cubit<ATxBroadcastState> {
Expand All @@ -21,8 +21,8 @@ class TxBroadcastCubit extends Cubit<ATxBroadcastState> {
Future<void> broadcast(SignedTxModel signedTxModel) async {
emit(TxBroadcastLoadingState());
try {
BroadcastRespModel broadcastRespModel = await broadcastService.broadcastTx(signedTxModel);
emit(TxBroadcastCompletedState(broadcastRespModel: broadcastRespModel));
BroadcastResp broadcastRespModel = await broadcastService.broadcastTx(signedTxModel);
emit(TxBroadcastCompletedState(broadcastResp: broadcastRespModel));
} on DioConnectException catch (dioConnectException) {
ErrorExplorerModel errorExplorerModel = ErrorExplorerModel.fromDioConnectException(dioConnectException);
emit(TxBroadcastErrorState(errorExplorerModel: errorExplorerModel));
Expand All @@ -33,8 +33,8 @@ class TxBroadcastCubit extends Cubit<ATxBroadcastState> {
RequestOptions requestOptions = txBroadcastException.response.requestOptions;

ErrorExplorerModel errorExplorerModel = ErrorExplorerModel(
code: txBroadcastException.broadcastErrorLogModel.code,
message: txBroadcastException.broadcastErrorLogModel.message,
code: txBroadcastException.broadcastResp?.code.toString() ?? 'Unknown',
message: txBroadcastException.broadcastResp?.log ?? 'Transaction broadcast failed',
uri: requestOptions.uri,
method: requestOptions.method,
request: requestOptions.data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class TxFormBuilderCubit extends Cubit<ATxFormBuilderState> {
Future<TxRemoteInfoModel> _downloadTxRemoteInfo() async {
assert(_authCubit.isSignedIn, 'Wallet public address must be provided to use this method');
try {
TxRemoteInfoModel txRemoteInfoModel = await _queryAccountService.getTxRemoteInfo(_authCubit.state!.address.bech32Address);
TxRemoteInfoModel txRemoteInfoModel =
await _queryAccountService.getTxRemoteInfo(_authCubit.state!.address.bech32Address);
return txRemoteInfoModel;
} on DioException catch (e) {
throw Exception('Cannot download TxRemoteInfoModel: ${e.message}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class TxProcessCubit<T extends AMsgFormModel> extends Cubit<ATxProcessState> {
String msgTypeName = InterxMsgTypes.getName(txMsgType);

try {
bool txRemoteInfoAvailableBool = await _queryAccountService.isAccountRegistered(authCubit.state!.address.bech32Address);
bool txRemoteInfoAvailableBool =
await _queryAccountService.isAccountRegistered(authCubit.state!.address.bech32Address);
if (txRemoteInfoAvailableBool == false) {
emit(const TxProcessErrorState(accountErrorBool: true));
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ class ListLoadedState<T> extends AListState {
final DateTime blockDateTime;
final DateTime cacheExpirationDateTime;

const ListLoadedState({
ListLoadedState({
required this.listItems,
required this.lastPage,
required this.blockDateTime,
required this.cacheExpirationDateTime,
});
DateTime? cacheExpirationDateTime,
}) : // TODO: #29
cacheExpirationDateTime = cacheExpirationDateTime ?? DateTime.now().add(const Duration(seconds: 30));

@override
List<Object?> get props => <Object?>[listItems, lastPage, blockDateTime, cacheExpirationDateTime];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class InfinityListBloc<T extends AListItem> extends AListBloc<T> {
listItems: visibleListItems,
lastPage: currentPageData.lastPageBool,
blockDateTime: currentPageData.blockDateTime!,
cacheExpirationDateTime: currentPageData.cacheExpirationDateTime!,
cacheExpirationDateTime: currentPageData.cacheExpirationDateTime,
));
showLoadingOverlay.value = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class PaginatedListBloc<T extends AListItem> extends AListBloc<T> {
add(const ListNextPageEvent());
}

void _mapPreviousPageEventToState(PaginatedListPreviousPageEvent paginatedListPreviousPageEvent, Emitter<AListState> emit) {
void _mapPreviousPageEventToState(
PaginatedListPreviousPageEvent paginatedListPreviousPageEvent, Emitter<AListState> emit) {
if (showLoadingOverlay.value) {
return;
}
Expand All @@ -66,7 +67,6 @@ class PaginatedListBloc<T extends AListItem> extends AListBloc<T> {
listItems: currentPageData.listItems,
lastPageBool: currentPageData.lastPageBool,
blockDateTime: currentPageData.blockDateTime!,
cacheExpirationDateTime: currentPageData.cacheExpirationDateTime!,
));

showLoadingOverlay.value = false;
Expand All @@ -91,7 +91,7 @@ class PaginatedListBloc<T extends AListItem> extends AListBloc<T> {
listItems: currentPageItems,
lastPageBool: currentPageItems.length < singlePageSize,
blockDateTime: downloadedPagesCache.values.first.blockDateTime,
cacheExpirationDateTime: downloadedPagesCache.values.first.cacheExpirationDateTime,
cacheExpirationDateTime: downloadedPagesCache.values.first.cacheExpirationDateTime,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import 'package:miro/blocs/widgets/kira/kira_list/abstract_list/states/list_load
class PaginatedListLoadedState<T> extends ListLoadedState<T> {
final int pageIndex;

const PaginatedListLoadedState({
PaginatedListLoadedState({
required this.pageIndex,
required bool lastPageBool,
required List<T> listItems,
required DateTime blockDateTime,
required DateTime cacheExpirationDateTime,
DateTime? cacheExpirationDateTime,
}) : super(
lastPage: lastPageBool,
listItems: listItems,
Expand Down
Loading
Loading