Skip to content
This repository has been archived by the owner on Feb 9, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into license-attributation
Browse files Browse the repository at this point in the history
  • Loading branch information
Hjort committed Nov 30, 2023
2 parents 3e13854 + a61b5c9 commit d90d7c0
Show file tree
Hide file tree
Showing 18 changed files with 516 additions and 199 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Analyze and format
name: Analyze, format and test

on:
# Trigger the workflow on pushes to the main and feature branches as well as PRs targeting them.
Expand Down Expand Up @@ -49,3 +49,20 @@ jobs:

- name: Check format
run: dart format --output=none --set-exit-if-changed . -l 150

test:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.flutter_version }}
channel: 'stable'
cache: true

- name: Test
run: flutter test

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release

# Hive testing
/test/hive_storage_test
12 changes: 6 additions & 6 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
PODS:
- Flutter (1.0.0)
- shared_preferences_foundation (0.0.1):
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter

DEPENDENCIES:
- Flutter (from `Flutter`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)

EXTERNAL SOURCES:
Flutter:
:path: Flutter
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"

SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b

PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189

Expand Down
16 changes: 16 additions & 0 deletions lib/entities/accepted_terms_and_conditions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:hive_flutter/hive_flutter.dart';

part 'accepted_terms_and_conditions.g.dart';

/// Version of the Terms & Conditions accepted by the user and time of acceptance.
@HiveType(typeId: 1)
class AcceptedTermsAndConditionsEntity {
static const table = "accepted_terms_and_conditions";

@HiveField(0)
final String version;
@HiveField(1)
final DateTime acceptedAt;

const AcceptedTermsAndConditionsEntity({required this.version, required this.acceptedAt});
}
41 changes: 41 additions & 0 deletions lib/entities/accepted_terms_and_conditions.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

174 changes: 131 additions & 43 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import 'package:concordium_wallet/repositories/terms_and_conditions_repository.dart';
import 'package:concordium_wallet/screens/routes.dart';
import 'package:concordium_wallet/services/http.dart';
import 'package:concordium_wallet/services/shared_preferences/service.dart';
import 'package:concordium_wallet/providers/storage.dart';
import 'package:concordium_wallet/services/wallet_proxy/service.dart';
import 'package:concordium_wallet/state/config.dart';
import 'package:concordium_wallet/state/network.dart';
import 'package:concordium_wallet/state/services.dart';
import 'package:concordium_wallet/state/terms_and_conditions.dart';
import 'package:concordium_wallet/theme.dart';
import 'package:concordium_wallet/types/future_value.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
runApp(const App());
Expand All @@ -33,13 +34,13 @@ Future<Config> loadConfig(HttpService http) async {
Future<ServiceRepository> bootstrap() async {
const http = HttpService();
final configFuture = loadConfig(http);
final prefsFuture = SharedPreferences.getInstance();
final storageFuture = StorageProvider.init();
final config = await configFuture;
final prefs = await prefsFuture;
final storageService = await storageFuture;
return ServiceRepository(
config: config,
http: http,
sharedPreferences: SharedPreferencesService(prefs),
storage: storageService,
);
}

Expand All @@ -50,59 +51,146 @@ class App extends StatelessWidget {

@override
Widget build(BuildContext context) {
// Initialize configuration and service repository.
return _WithServiceRepository(
child: _WithSelectedNetwork(
initialNetwork: initialNetwork,
child: _WithTermsAndConditionAcceptance(
child: MaterialApp(
routes: appRoutes,
theme: concordiumTheme(),
)),
),
);
}
}

class _WithServiceRepository extends StatefulWidget {
final Widget child;

const _WithServiceRepository({required this.child});

@override
State<_WithServiceRepository> createState() => _WithServiceRepositoryState();
}

class _WithServiceRepositoryState extends State<_WithServiceRepository> {
late final Future<ServiceRepository> _bootstrapping;

@override
void initState() {
super.initState();
setState(() {
_bootstrapping = bootstrap();
});
}

@override
Widget build(BuildContext context) {
return FutureBuilder<ServiceRepository>(
future: bootstrap(),
future: _bootstrapping,
builder: (_, snapshot) {
final services = snapshot.data;
if (services == null) {
// Initializing configuration and service repository.
return const _Initializing();
}
// Provide initialized service repository to the nested components
// (including the blocs created in the child provider).
// Then activate the initial network (starting services related to that network).
return RepositoryProvider(
create: (_) => services,
child: FutureBuilder(
future: services.activateNetwork(initialNetwork),
builder: (_, snapshot) {
final networkServices = snapshot.data;
if (networkServices == null) {
// Initializing network services.
return const _Initializing();
}

// Initialize blocs/cubits.
return MultiBlocProvider(
providers: [
BlocProvider(
create: (_) {
// Initialize selected network as the one that was just activated.
return SelectedNetwork(networkServices);
},
),
BlocProvider(
create: (context) {
// Initialize T&C by loading the currently accepted version from shared preferences.
final prefs = context.read<ServiceRepository>().sharedPreferences;
return TermsAndConditionAcceptance(prefs);
},
),
],
child: MaterialApp(
routes: appRoutes,
theme: concordiumTheme(),
),
);
},
),
child: widget.child,
);
},
);
}
}

class _WithSelectedNetwork extends StatefulWidget {
final NetworkName initialNetwork;
final Widget child;

const _WithSelectedNetwork({required this.initialNetwork, required this.child});

@override
State<_WithSelectedNetwork> createState() => _WithSelectedNetworkState();
}

class _WithSelectedNetworkState extends State<_WithSelectedNetwork> {
late final Future<NetworkServices> _activating;

@override
void initState() {
super.initState();
final services = context.read<ServiceRepository>();
setState(() {
_activating = services.activateNetwork(initialNetwork);
});
}

@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _activating,
builder: (_, snapshot) {
final networkServices = snapshot.data;
if (networkServices == null) {
// Initializing network services.
return const _Initializing();
}

// Initialize blocs/cubits.
return BlocProvider(
create: (_) {
// Initialize selected network as the one that was just activated.
return SelectedNetwork(networkServices);
},
child: widget.child,
);
},
);
}
}

class _WithTermsAndConditionAcceptance extends StatefulWidget {
final Widget child;

const _WithTermsAndConditionAcceptance({required this.child});

@override
State<_WithTermsAndConditionAcceptance> createState() => _WithTermsAndConditionAcceptanceState();
}

class _WithTermsAndConditionAcceptanceState extends State<_WithTermsAndConditionAcceptance> {
late final Future<FutureValue<AcceptedTermsAndConditions?>> _lastAccepted;
late final TermsAndConditionsRepository _repository;

@override
void initState() {
super.initState();
final storage = context.read<ServiceRepository>().storage;
setState(() {
_repository = TermsAndConditionsRepository(storageProvider: storage);
_lastAccepted = _repository.getAcceptedTermsAndConditions().then(FutureValue.new);
});
}

@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _lastAccepted,
builder: (_, snapshot) {
if (snapshot.data != null) {
return BlocProvider(
create: (_) {
return TermsAndConditionAcceptance(_repository, snapshot.requireData.value);
},
child: widget.child);
} else if (snapshot.hasError) {
// TODO Handle error
}
return const _Initializing();
});
}
}

class _Initializing extends StatelessWidget {
const _Initializing();

Expand Down
Loading

0 comments on commit d90d7c0

Please sign in to comment.