Skip to content
Closed
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
12 changes: 9 additions & 3 deletions learning/tour-of-beam/frontend/assets/translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
ui:
about: About Tour of Beam
builtWith: Built with Apache Beam
cancel: Cancel
continueGitHub: Continue with GitHub
continueGoogle: Continue with Google
copyright: © The Apache Software Foundation
hint: Hint
deleteAccount: Delete my account
deleteMyAccount: Delete my account
deleteTobAccount: Delete my Tour of Beam account
privacyPolicy: Privacy Policy
reportIssue: Report Issue in GitHub
signIn: Sign in
signOut: Sign out
solution: Solution
toWebsite: To Apache Beam website

pages:
Expand All @@ -38,11 +38,17 @@ pages:
startTour: Start your tour
title: Welcome to the Tour of Beam!
tour:
assignment: Assignment
completeUnit: Complete Unit
hint: Hint
showSolution: Show the solution
solution: Solution
solveYourself: Before revealing the solution, try solving the challenge on your own. Remember, the more you practice, the better you will become. Give it a shot and see how far you can get.
summaryTitle: Table of Contents

dialogs:
signInIf: If you would like to save your progress and track completed modules
deleteAccountWarning: Are you sure you want to delete your Tour of Beam account? This will permanently erase your learning progress.

complexity:
basic: Basic level
Expand Down
22 changes: 20 additions & 2 deletions learning/tour-of-beam/frontend/lib/auth/notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:playground_components/playground_components.dart';

import '../repositories/client/client.dart';

class AuthNotifier extends ChangeNotifier {
AuthNotifier() {
final TobClient client;

AuthNotifier({required this.client}) {
FirebaseAuth.instance.authStateChanges().listen((user) {
notifyListeners();
});
Expand All @@ -36,10 +41,23 @@ class AuthNotifier extends ChangeNotifier {
}

Future<void> logIn(AuthProvider authProvider) async {
await FirebaseAuth.instance.signInWithPopup(authProvider);
try {
await FirebaseAuth.instance.signInWithPopup(authProvider);
} on Exception catch (e) {
PlaygroundComponents.toastNotifier.addException(e);
}
}

Future<void> logOut() async {
await FirebaseAuth.instance.signOut();
}

Future<void> deleteAccount() async {
try {
await client.postDeleteUserProgress();
await FirebaseAuth.instance.currentUser?.delete();
} on Exception catch (e) {
PlaygroundComponents.toastNotifier.addException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class LoginButton extends StatelessWidget {
return TextButton(
onPressed: () {
final closeNotifier = PublicNotifier();
openOverlay(
showOverlay(
context: context,
closeNotifier: closeNotifier,
positioned: Positioned(
Expand Down
16 changes: 10 additions & 6 deletions learning/tour-of-beam/frontend/lib/components/login/content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,13 @@ class _BrandedLoginButtons extends StatelessWidget {
required this.onLoggedIn,
});

Future<void> _logIn(AuthProvider authProvider) async {
await GetIt.instance.get<AuthNotifier>().logIn(authProvider);
onLoggedIn();
}

@override
Widget build(BuildContext context) {
final authNotifier = GetIt.instance.get<AuthNotifier>();

final isLightTheme = Theme.of(context).brightness == Brightness.light;
final textStyle =
MaterialStatePropertyAll(Theme.of(context).textTheme.bodyMedium);
Expand Down Expand Up @@ -124,16 +127,17 @@ class _BrandedLoginButtons extends StatelessWidget {
return Column(
children: [
ElevatedButton.icon(
onPressed: () {},
onPressed: () {
_logIn(GithubAuthProvider());
},
style: isLightTheme ? githubLightButtonStyle : darkButtonStyle,
icon: SvgPicture.asset(Assets.svg.githubLogo),
label: const Text('ui.continueGitHub').tr(),
),
const SizedBox(height: BeamSizes.size16),
ElevatedButton.icon(
onPressed: () async {
await authNotifier.logIn(GoogleAuthProvider());
onLoggedIn();
onPressed: () {
_logIn(GoogleAuthProvider());
},
style: isLightTheme ? googleLightButtonStyle : darkButtonStyle,
icon: SvgPicture.asset(Assets.svg.googleLogo),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Avatar extends StatelessWidget {
return GestureDetector(
onTap: () {
final closeNotifier = PublicNotifier();
openOverlay(
showOverlay(
context: context,
closeNotifier: closeNotifier,
positioned: Positioned(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,24 @@ class _Buttons extends StatelessWidget {
),
const BeamDivider(),
_IconLabel(
onTap: () {},
onTap: () async {
closeOverlayCallback();
final confirmed = await ConfirmDialog.show(
context: context,
confirmButtonText: 'ui.deleteMyAccount'.tr(),
subtitle: 'dialogs.deleteAccountWarning'.tr(),
title: 'ui.deleteTobAccount'.tr(),
);
if (confirmed) {
ProgressDialog.show(
future: authNotifier.deleteAccount(),
navigatorKey:
GetIt.instance.get<BeamRouterDelegate>().navigatorKey!,
);
}
},
iconPath: Assets.svg.profileDelete,
label: 'ui.deleteAccount'.tr(),
label: 'ui.deleteMyAccount'.tr(),
),
],
);
Expand Down
1 change: 0 additions & 1 deletion learning/tour-of-beam/frontend/lib/constants/sizes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
class TobSizes {
static const double footerHeight = 35;
static const double authOverlayWidth = 260;
static const double hintPopupWidth = 420;
}

class ScreenSizes {
Expand Down
31 changes: 16 additions & 15 deletions learning/tour-of-beam/frontend/lib/locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import 'package:app_state/app_state.dart';
import 'package:get_it/get_it.dart';
import 'package:playground_components/playground_components.dart';

import 'auth/notifier.dart';
import 'cache/content_tree.dart';
Expand All @@ -31,33 +32,33 @@ import 'router/page_factory.dart';
import 'router/route_information_parser.dart';
import 'state.dart';

final _client = CloudFunctionsTobClient();

Future<void> initializeServiceLocator() async {
_initializeAuth();
_initializeState();
_initializeCaches();
}

void _initializeAuth() {
GetIt.instance.registerSingleton(AuthNotifier());
GetIt.instance.registerSingleton(AuthNotifier(client: _client));
}

void _initializeCaches() {
final client = CloudFunctionsTobClient();

GetIt.instance.registerSingleton<TobClient>(client);
GetIt.instance.registerSingleton(ContentTreeCache(client: client));
GetIt.instance.registerSingleton(SdkCache(client: client));
GetIt.instance.registerSingleton(UnitContentCache(client: client));
GetIt.instance.registerSingleton(UnitProgressCache(client: client));
GetIt.instance.registerSingleton<TobClient>(_client);
GetIt.instance.registerSingleton(ContentTreeCache(client: _client));
GetIt.instance.registerSingleton(SdkCache(client: _client));
GetIt.instance.registerSingleton(UnitContentCache(client: _client));
GetIt.instance.registerSingleton(UnitProgressCache(client: _client));
}

void _initializeState() {
GetIt.instance.registerSingleton(AppNotifier());
GetIt.instance.registerSingleton(
PageStack(
bottomPage: WelcomePage(),
createPage: PageFactory.createPage,
routeInformationParser: TobRouteInformationParser(),
),
final pageStack = PageStack(
bottomPage: WelcomePage(),
createPage: PageFactory.createPage,
routeInformationParser: TobRouteInformationParser(),
);
GetIt.instance.registerSingleton(AppNotifier());
GetIt.instance.registerSingleton(pageStack);
GetIt.instance.registerSingleton(BeamRouterDelegate(pageStack));
}
2 changes: 1 addition & 1 deletion learning/tour-of-beam/frontend/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void main() async {
const englishLocale = Locale('en');

final pageStack = GetIt.instance.get<PageStack>();
final routerDelegate = PageStackRouterDelegate(pageStack);
final routerDelegate = GetIt.instance.get<BeamRouterDelegate>();
final routeInformationParser = TobRouteInformationParser();
final backButtonDispatcher = PageStackBackButtonDispatcher(pageStack);

Expand Down
4 changes: 2 additions & 2 deletions learning/tour-of-beam/frontend/lib/pages/tour/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,11 @@ class TourNotifier extends ChangeNotifier with PageStateMixin<void> {
}

@override
void dispose() {
Future<void> dispose() async {
_unitContentCache.removeListener(_onUnitChanged);
contentTreeController.removeListener(_onUnitChanged);
_appNotifier.removeListener(_onAppNotifierChanged);
_authNotifier.removeListener(_onUnitProgressChanged);
super.dispose();
await super.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import 'package:flutter_svg/svg.dart';
import 'package:playground_components/playground_components.dart';

import '../../../assets/assets.gen.dart';
import '../../../constants/sizes.dart';
import 'markdown/tob_markdown.dart';

class HintsWidget extends StatelessWidget {
Expand All @@ -47,7 +46,7 @@ class HintsWidget extends StatelessWidget {
}
},
icon: SvgPicture.asset(Assets.svg.hint),
label: const Text('ui.hint').tr(),
label: const Text('pages.tour.hint').tr(),
);
}
}
Expand All @@ -63,14 +62,14 @@ class _Popup extends StatelessWidget {
Widget build(BuildContext context) {
return OverlayBody(
child: Container(
width: TobSizes.hintPopupWidth,
width: BeamSizes.popupWidth,
padding: const EdgeInsets.all(BeamSizes.size16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'ui.hint',
'pages.tour.hint',
style: Theme.of(context).textTheme.headlineLarge,
).tr(),
const SizedBox(height: BeamSizes.size8),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:playground_components/playground_components.dart';

import '../../../assets/assets.gen.dart';
import '../state.dart';
Expand All @@ -42,9 +43,24 @@ class SolutionButton extends StatelessWidget {
: null,
),
),
onPressed: tourNotifier.toggleShowingSolution,
onPressed: () async {
// TODO(nausharipov): resolve the conflict with save user code
if (tourNotifier.isShowingSolution) {
tourNotifier.toggleShowingSolution();
} else {
final confirmed = await ConfirmDialog.show(
context: context,
confirmButtonText: 'pages.tour.showSolution'.tr(),
subtitle: 'pages.tour.solveYourself'.tr(),
title: 'pages.tour.solution'.tr(),
);
if (confirmed) {
tourNotifier.toggleShowingSolution();
}
}
},
icon: SvgPicture.asset(Assets.svg.solution),
label: const Text('ui.solution').tr(),
label: const Text('pages.tour.solution').tr(),
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ abstract class TobClient {
Future<GetUserProgressResponse?> getUserProgress(String sdkId);

Future<void> postUnitComplete(String sdkId, String id);

Future<void> postDeleteUserProgress();
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,17 @@ class CloudFunctionsTobClient extends TobClient {
},
);
}

@override
Future<void> postDeleteUserProgress() async {
final token = await GetIt.instance.get<AuthNotifier>().getToken();
await http.post(
Uri.parse(
'$cloudFunctionsBaseUrl/postDeleteProgress',
),
headers: {
HttpHeaders.authorizationHeader: 'Bearer $token',
},
);
}
}
Loading