Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test

on:
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter --version
- run: flutter test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ app.*.map.json
**/secrets/*.key
**/secrets/*.json
**/secrets/*.yaml

android/app/.cxx
android/build
32 changes: 32 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldb_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Generated file, do not edit.
#

import lldb

def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()

# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'

error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return

def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")
5 changes: 5 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#

command script import --relative-to-command-file flutter_lldb_helper.py
43 changes: 43 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
use_frameworks!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
144 changes: 144 additions & 0 deletions lib/application/proposal_manager/proposal_manager_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import 'dart:async';
import 'package:hnotes/infrastructure/blockchain/contract_repository.dart';
import 'package:hnotes/infrastructure/constants.dart';

class ProposalManagerBloc {
static const String contractAddress =
"mantra17p9u09rgfd2nwr52ayy0aezdc42r2xd2g5d70u00k5qyhzjqf89q08tazu";

late final ContractRepository _contractRepository;

// Stream controllers
final StreamController<Map<String, dynamic>?> _contractStatusController =
StreamController<Map<String, dynamic>?>.broadcast();
final StreamController<List<Map<String, dynamic>>> _proposalsController =
StreamController<List<Map<String, dynamic>>>.broadcast();
final StreamController<Map<String, dynamic>?> _contractConfigController =
StreamController<Map<String, dynamic>?>.broadcast();

// Stream getters
Stream<Map<String, dynamic>?> get contractStatusStream =>
_contractStatusController.stream;

Stream<List<Map<String, dynamic>>> get proposalsStream =>
_proposalsController.stream;

Stream<Map<String, dynamic>?> get contractConfigStream =>
_contractConfigController.stream;

ProposalManagerBloc() {
_contractRepository = ContractRepository(
rpcEndpoint: chainRpcUrl,
restEndpoint: chainRestUrl,
contractAddress: contractAddress,
);

// Initialize streams with empty/null values to prevent null errors
_contractStatusController.add(null);
_proposalsController.add([]);
_contractConfigController.add(null);
}

/// Load all contract data (status, config, proposals)
Future<void> loadContractData() async {
try {
// Load contract status
await loadContractStatus();

// Load contract config
await loadContractConfig();

// Load proposals
await loadProposals();
} catch (e) {
_contractStatusController.addError(e);
_proposalsController.addError(e);
_contractConfigController.addError(e);
}
}

/// Load contract status
Future<void> loadContractStatus() async {
try {
final status = await _contractRepository.queryContract({'status': {}});
_contractStatusController.add(status);
} catch (e) {
_contractStatusController.addError(e);
}
}

/// Load contract configuration
Future<void> loadContractConfig() async {
try {
final config = await _contractRepository.queryContract({'config': {}});
_contractConfigController.add(config);
} catch (e) {
_contractConfigController.addError(e);
}
}

/// Load all proposals
Future<void> loadProposals() async {
try {
final result = await _contractRepository.queryContract({'proposals': {}});
if (result != null && result['proposals'] is List) {
final proposals = (result['proposals'] as List)
.map((proposal) => proposal as Map<String, dynamic>)
.toList();
_proposalsController.add(proposals);
} else {
_proposalsController.add([]);
}
} catch (e) {
_proposalsController.addError(e);
}
}

/// Get specific proposal by ID
Future<Map<String, dynamic>?> getProposal(int proposalId) async {
try {
return await _contractRepository.queryContract({
'proposal': {'id': proposalId},
});
} catch (e) {
rethrow;
}
}

/// Get contract ownership information
Future<Map<String, dynamic>?> getOwnership() async {
try {
return await _contractRepository.queryContract({'ownership': {}});
} catch (e) {
rethrow;
}
}

/// Test connection to the contract
Future<bool> testConnection() async {
try {
return await _contractRepository.testConnection();
} catch (e) {
return false;
}
}

/// Get contract info
Future<Map<String, dynamic>?> getContractInfo() async {
try {
return await _contractRepository.getContractInfo();
} catch (e) {
rethrow;
}
}

/// Dispose resources
void dispose() {
_contractStatusController.close();
_proposalsController.close();
_contractConfigController.close();
}
}

// Global instance
final proposalManagerBloc = ProposalManagerBloc();
Loading