Merge branch 'main' into feat/multiball-asset

pull/235/head
RuiAlonso 3 years ago
commit 17091e1cf3

@ -0,0 +1,22 @@
name: authentication_repository
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
paths:
- "packages/authentication_repository/**"
- ".github/workflows/authentication_repository.yaml"
pull_request:
paths:
- "packages/authentication_repository/**"
- ".github/workflows/authentication_repository.yaml"
jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
working_directory: packages/authentication_repository

@ -0,0 +1,39 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# VSCode related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json

@ -0,0 +1,11 @@
# authentication_repository
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![License: MIT][license_badge]][license_link]
Repository to manage user authentication.
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license_link]: https://opensource.org/licenses/MIT
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis

@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.2.4.0.yaml

@ -0,0 +1,3 @@
library authentication_repository;
export 'src/authentication_repository.dart';

@ -0,0 +1,36 @@
import 'package:firebase_auth/firebase_auth.dart';
/// {@template authentication_exception}
/// Exception for authentication repository failures.
/// {@endtemplate}
class AuthenticationException implements Exception {
/// {@macro authentication_exception}
const AuthenticationException(this.error, this.stackTrace);
/// The error that was caught.
final Object error;
/// The Stacktrace associated with the [error].
final StackTrace stackTrace;
}
/// {@template authentication_repository}
/// Repository to manage user authentication.
/// {@endtemplate}
class AuthenticationRepository {
/// {@macro authentication_repository}
AuthenticationRepository(this._firebaseAuth);
final FirebaseAuth _firebaseAuth;
/// Sign in the existing user anonymously using [FirebaseAuth]. If the
/// authentication process can't be completed, it will throw an
/// [AuthenticationException].
Future<void> authenticateAnonymously() async {
try {
await _firebaseAuth.signInAnonymously();
} on Exception catch (error, stackTrace) {
throw AuthenticationException(error, stackTrace);
}
}
}

@ -0,0 +1,18 @@
name: authentication_repository
description: Repository to manage user authentication.
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=2.16.0 <3.0.0"
dependencies:
firebase_auth: ^3.3.16
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^0.2.0
very_good_analysis: ^2.4.0

@ -0,0 +1,40 @@
import 'package:authentication_repository/authentication_repository.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockFirebaseAuth extends Mock implements FirebaseAuth {}
class MockUserCredential extends Mock implements UserCredential {}
void main() {
late FirebaseAuth firebaseAuth;
late UserCredential userCredential;
late AuthenticationRepository authenticationRepository;
group('AuthenticationRepository', () {
setUp(() {
firebaseAuth = MockFirebaseAuth();
userCredential = MockUserCredential();
authenticationRepository = AuthenticationRepository(firebaseAuth);
});
group('authenticateAnonymously', () {
test('completes if no exception is thrown', () async {
when(() => firebaseAuth.signInAnonymously())
.thenAnswer((_) async => userCredential);
await authenticationRepository.authenticateAnonymously();
verify(() => firebaseAuth.signInAnonymously()).called(1);
});
test('throws AuthenticationException when firebase auth fails', () async {
when(() => firebaseAuth.signInAnonymously())
.thenThrow(Exception('oops'));
expect(
() => authenticationRepository.authenticateAnonymously(),
throwsA(isA<AuthenticationException>()),
);
});
});
});
}

@ -1,95 +1,105 @@
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
/// Appends a new [ContactCallbacks] to the parent. /// Appends a new [ContactCallbacks] to the parent.
/// ///
/// This is a convenience class for adding a [ContactCallbacks] to the parent. /// This is a convenience class for adding a [ContactCallbacks] to the parent.
/// In constract with just adding a [ContactCallbacks] to the parent's body /// In contrast with just assigning a [ContactCallbacks] to a userData, this
/// userData, this class respects the previous [ContactCallbacks] in the /// class respects the previous userData.
/// parent's body userData, if any. Hence, it avoids overriding any previous
/// [ContactCallbacks] in the parent.
/// ///
/// It does so by grouping the [ContactCallbacks] in a [_ContactCallbacksGroup], /// It does so by grouping the userData in a [_UserData], and resetting the
/// and resetting the parent's userData accordingly. /// parent's userData accordingly.
// TODO(alestiago): Make use of generics to infer the type of the contact. // TODO(alestiago): Make use of generics to infer the type of the contact.
// https://github.com/VGVentures/pinball/pull/234#discussion_r859182267 // https://github.com/VGVentures/pinball/pull/234#discussion_r859182267
// TODO(alestiago): Consider if there is a need to support adjusting a fixture's
// userData.
class ContactBehavior<T extends BodyComponent> extends Component class ContactBehavior<T extends BodyComponent> extends Component
with ContactCallbacks, ParentIsA<T> { with ContactCallbacks, ParentIsA<T> {
final _fixturesUserData = <Object>{};
/// Specifies which fixtures should be considered for contact.
///
/// Fixtures are identifiable by their userData.
///
/// If no fixtures are specified, the [ContactCallbacks] is applied to the
/// entire body, hence all fixtures are considered.
void applyTo(Iterable<Object> userData) => _fixturesUserData.addAll(userData);
@override @override
@mustCallSuper
Future<void> onLoad() async { Future<void> onLoad() async {
final userData = parent.body.userData; if (_fixturesUserData.isNotEmpty) {
if (userData is _ContactCallbacksGroup) { for (final fixture in _targetedFixtures) {
userData.addContactCallbacks(this); fixture.userData = _UserData.fromFixture(fixture)..add(this);
} else if (userData is ContactCallbacks) { }
final contactCallbacksGroup = _ContactCallbacksGroup()
..addContactCallbacks(userData)
..addContactCallbacks(this);
parent.body.userData = contactCallbacksGroup;
} else { } else {
parent.body.userData = this; parent.body.userData = _UserData.fromBody(parent.body)..add(this);
}
}
Iterable<Fixture> get _targetedFixtures =>
parent.body.fixtures.where((fixture) {
if (_fixturesUserData.contains(fixture.userData)) return true;
final userData = fixture.userData;
if (userData is _UserData) {
return _fixturesUserData.contains(userData.value);
} }
return false;
});
} }
class _UserData with ContactCallbacks {
_UserData._(Object? userData) : _userData = [userData];
factory _UserData._fromUserData(Object? userData) {
if (userData is _UserData) return userData;
return _UserData._(userData);
} }
class _ContactCallbacksGroup implements ContactCallbacks { factory _UserData.fromFixture(Fixture fixture) =>
final List<ContactCallbacks> _contactCallbacks = []; _UserData._fromUserData(fixture.userData);
factory _UserData.fromBody(Body body) =>
_UserData._fromUserData(body.userData);
final List<Object?> _userData;
Iterable<ContactCallbacks> get _contactCallbacks =>
_userData.whereType<ContactCallbacks>();
Object? get value => _userData.first;
void add(Object? userData) => _userData.add(userData);
@override @override
@mustCallSuper
void beginContact(Object other, Contact contact) { void beginContact(Object other, Contact contact) {
onBeginContact?.call(other, contact); super.beginContact(other, contact);
for (final callback in _contactCallbacks) { for (final callback in _contactCallbacks) {
callback.beginContact(other, contact); callback.beginContact(other, contact);
} }
} }
@override @override
@mustCallSuper
void endContact(Object other, Contact contact) { void endContact(Object other, Contact contact) {
onEndContact?.call(other, contact); super.endContact(other, contact);
for (final callback in _contactCallbacks) { for (final callback in _contactCallbacks) {
callback.endContact(other, contact); callback.endContact(other, contact);
} }
} }
@override @override
@mustCallSuper
void preSolve(Object other, Contact contact, Manifold oldManifold) { void preSolve(Object other, Contact contact, Manifold oldManifold) {
onPreSolve?.call(other, contact, oldManifold); super.preSolve(other, contact, oldManifold);
for (final callback in _contactCallbacks) { for (final callback in _contactCallbacks) {
callback.preSolve(other, contact, oldManifold); callback.preSolve(other, contact, oldManifold);
} }
} }
@override @override
@mustCallSuper
void postSolve(Object other, Contact contact, ContactImpulse impulse) { void postSolve(Object other, Contact contact, ContactImpulse impulse) {
onPostSolve?.call(other, contact, impulse); super.postSolve(other, contact, impulse);
for (final callback in _contactCallbacks) { for (final callback in _contactCallbacks) {
callback.postSolve(other, contact, impulse); callback.postSolve(other, contact, impulse);
} }
} }
void addContactCallbacks(ContactCallbacks callback) {
_contactCallbacks.add(callback);
}
@override
void Function(Object other, Contact contact)? onBeginContact;
@override
void Function(Object other, Contact contact)? onEndContact;
@override
void Function(Object other, Contact contact, ContactImpulse impulse)?
onPostSolve;
@override
void Function(Object other, Contact contact, Manifold oldManifold)?
onPreSolve;
} }

@ -58,28 +58,78 @@ void main() {
late Contact contact; late Contact contact;
late Manifold manifold; late Manifold manifold;
late ContactImpulse contactImpulse; late ContactImpulse contactImpulse;
late FixtureDef fixtureDef;
setUp(() { setUp(() {
other = Object(); other = Object();
contact = _MockContact(); contact = _MockContact();
manifold = _MockManifold(); manifold = _MockManifold();
contactImpulse = _MockContactImpulse(); contactImpulse = _MockContactImpulse();
fixtureDef = FixtureDef(CircleShape());
}); });
flameTester.test( flameTester.test(
'should add a new ContactCallbacks to the parent', "should add a new ContactCallbacks to the parent's body userData "
'when not applied to fixtures',
(game) async { (game) async {
final parent = _TestBodyComponent(); final parent = _TestBodyComponent();
final contactBehavior = ContactBehavior(); final contactBehavior = ContactBehavior();
await parent.add(contactBehavior); await parent.add(contactBehavior);
await game.ensureAdd(parent); await game.ensureAdd(parent);
expect(parent.body.userData, contactBehavior); expect(parent.body.userData, isA<ContactCallbacks>());
}, },
); );
flameTester.test( flameTester.test(
"should respect the previous ContactCallbacks in the parent's userData", 'should add a new ContactCallbacks to the targeted fixture ',
(game) async {
final parent = _TestBodyComponent();
await game.ensureAdd(parent);
final fixture1 =
parent.body.createFixture(fixtureDef..userData = 'foo');
final fixture2 = parent.body.createFixture(fixtureDef..userData = null);
final contactBehavior = ContactBehavior()
..applyTo(
[fixture1.userData!],
);
await parent.ensureAdd(contactBehavior);
expect(parent.body.userData, isNull);
expect(fixture1.userData, isA<ContactCallbacks>());
expect(fixture2.userData, isNull);
},
);
flameTester.test(
'should add a new ContactCallbacks to the targeted fixtures ',
(game) async {
final parent = _TestBodyComponent();
await game.ensureAdd(parent);
final fixture1 =
parent.body.createFixture(fixtureDef..userData = 'foo');
final fixture2 =
parent.body.createFixture(fixtureDef..userData = 'boo');
final contactBehavior = ContactBehavior()
..applyTo([
fixture1.userData!,
fixture2.userData!,
]);
await parent.ensureAdd(contactBehavior);
expect(parent.body.userData, isNull);
expect(fixture1.userData, isA<ContactCallbacks>());
expect(fixture2.userData, isA<ContactCallbacks>());
},
);
flameTester.test(
"should respect the previous ContactCallbacks in the parent's userData "
'when not applied to fixtures',
(game) async { (game) async {
final parent = _TestBodyComponent(); final parent = _TestBodyComponent();
await game.ensureAdd(parent); await game.ensureAdd(parent);
@ -113,7 +163,8 @@ void main() {
}, },
); );
flameTester.test('can group multiple ContactBehaviors and keep listening', flameTester.test(
'can group multiple ContactBehaviors and keep listening',
(game) async { (game) async {
final parent = _TestBodyComponent(); final parent = _TestBodyComponent();
await game.ensureAdd(parent); await game.ensureAdd(parent);
@ -148,6 +199,58 @@ void main() {
expect(contactBehavior1.postSolveContactCallsCount, equals(1)); expect(contactBehavior1.postSolveContactCallsCount, equals(1));
expect(contactBehavior2.postSolveContactCallsCount, equals(1)); expect(contactBehavior2.postSolveContactCallsCount, equals(1));
expect(contactBehavior3.postSolveContactCallsCount, equals(1)); expect(contactBehavior3.postSolveContactCallsCount, equals(1));
}); },
);
flameTester.test(
'can group multiple ContactBehaviors and keep listening '
'when applied to a fixture',
(game) async {
final parent = _TestBodyComponent();
await game.ensureAdd(parent);
final fixture = parent.body.createFixture(fixtureDef..userData = 'foo');
final contactBehavior1 = _TestContactBehavior()
..applyTo(
[fixture.userData!],
);
final contactBehavior2 = _TestContactBehavior()
..applyTo(
[fixture.userData!],
);
final contactBehavior3 = _TestContactBehavior()
..applyTo(
[fixture.userData!],
);
await parent.ensureAddAll([
contactBehavior1,
contactBehavior2,
contactBehavior3,
]);
final contactCallbacks = fixture.userData! as ContactCallbacks;
contactCallbacks.beginContact(other, contact);
expect(contactBehavior1.beginContactCallsCount, equals(1));
expect(contactBehavior2.beginContactCallsCount, equals(1));
expect(contactBehavior3.beginContactCallsCount, equals(1));
contactCallbacks.endContact(other, contact);
expect(contactBehavior1.endContactCallsCount, equals(1));
expect(contactBehavior2.endContactCallsCount, equals(1));
expect(contactBehavior3.endContactCallsCount, equals(1));
contactCallbacks.preSolve(other, contact, manifold);
expect(contactBehavior1.preSolveContactCallsCount, equals(1));
expect(contactBehavior2.preSolveContactCallsCount, equals(1));
expect(contactBehavior3.preSolveContactCallsCount, equals(1));
contactCallbacks.postSolve(other, contact, contactImpulse);
expect(contactBehavior1.postSolveContactCallsCount, equals(1));
expect(contactBehavior2.postSolveContactCallsCount, equals(1));
expect(contactBehavior3.postSolveContactCallsCount, equals(1));
},
);
}); });
} }

@ -36,6 +36,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.20.1" version: "0.20.1"
authentication_repository:
dependency: "direct main"
description:
path: "packages/authentication_repository"
relative: true
source: path
version: "1.0.0+1"
bloc: bloc:
dependency: "direct main" dependency: "direct main"
description: description:
@ -169,6 +176,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.2" version: "6.1.2"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.16"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "6.2.4"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.13"
firebase_core: firebase_core:
dependency: transitive dependency: transitive
description: description:
@ -189,7 +217,7 @@ packages:
name: firebase_core_web name: firebase_core_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.1" version: "1.6.2"
flame: flame:
dependency: "direct main" dependency: "direct main"
description: description:

@ -7,9 +7,12 @@ environment:
sdk: ">=2.16.0 <3.0.0" sdk: ">=2.16.0 <3.0.0"
dependencies: dependencies:
authentication_repository:
path: packages/authentication_repository
bloc: ^8.0.2 bloc: ^8.0.2
cloud_firestore: ^3.1.10 cloud_firestore: ^3.1.10
equatable: ^2.0.3 equatable: ^2.0.3
firebase_auth: ^3.3.16
flame: ^1.1.1 flame: ^1.1.1
flame_bloc: ^1.2.0 flame_bloc: ^1.2.0
flame_forge2d: flame_forge2d:

Loading…
Cancel
Save