diff --git a/learning/tour-of-beam/frontend/assets/translations/en.yaml b/learning/tour-of-beam/frontend/assets/translations/en.yaml index ca8f929fdb73..919c2a39ae72 100644 --- a/learning/tour-of-beam/frontend/assets/translations/en.yaml +++ b/learning/tour-of-beam/frontend/assets/translations/en.yaml @@ -21,14 +21,13 @@ ui: continueGitHub: Continue with GitHub continueGoogle: Continue with Google copyright: © The Apache Software Foundation - darkMode: Dark Mode - lightMode: Light Mode privacyPolicy: Privacy Policy reportIssue: Report Issue in GitHub signIn: Sign in signOut: Sign out toWebsite: To Apache Beam website deleteAccount: Delete my account + pages: welcome: title: Welcome to the Tour of Beam! @@ -39,8 +38,10 @@ pages: tour: summaryTitle: Table of Contents completeUnit: Complete Unit + dialogs: signInIf: If you would like to save your progress and track completed modules + complexity: basic: Basic level medium: Medium level diff --git a/learning/tour-of-beam/frontend/lib/components/footer.dart b/learning/tour-of-beam/frontend/lib/components/footer.dart index fc7af8cd4691..e801836bb898 100644 --- a/learning/tour-of-beam/frontend/lib/components/footer.dart +++ b/learning/tour-of-beam/frontend/lib/components/footer.dart @@ -42,7 +42,7 @@ class Footer extends StatelessWidget { const Text('ui.copyright').tr(), ], ), - // TODO(nausharipov): get version + // TODO(nausharipov): get version, https://github.com/apache/beam/issues/23038 Text( '${'ui.builtWith'.tr()} (TODO: Version)', style: const TextStyle( @@ -61,6 +61,9 @@ class _Body extends StatelessWidget { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + final ext = themeData.extension()!; + return Container( width: double.infinity, height: TobSizes.footerHeight, @@ -69,9 +72,9 @@ class _Body extends StatelessWidget { horizontal: BeamSizes.size16, ), decoration: BoxDecoration( - color: ThemeColors.of(context).secondaryBackground, + color: ext.secondaryBackgroundColor, border: Border( - top: BorderSide(color: ThemeColors.of(context).divider), + top: BorderSide(color: themeData.dividerColor), ), ), child: child, diff --git a/learning/tour-of-beam/frontend/lib/components/split_view/widget.dart b/learning/tour-of-beam/frontend/lib/components/split_view/widget.dart deleted file mode 100644 index fb28cb4f7ee1..000000000000 --- a/learning/tour-of-beam/frontend/lib/components/split_view/widget.dart +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:playground_components/playground_components.dart'; -import 'package:split_view/split_view.dart'; - -import 'pan.dart'; - -class TobSplitView extends StatelessWidget { - final Axis direction; - final List pans; - - const TobSplitView({ - required this.direction, - required this.pans, - }); - - @override - Widget build(BuildContext context) { - return SplitView( - gripSize: BeamSizes.splitViewSeparator, - gripColor: ThemeColors.of(context).divider, - gripColorActive: ThemeColors.of(context).divider, - indicator: const DragIndicator(), - viewMode: direction == Axis.horizontal - ? SplitViewMode.Horizontal - : SplitViewMode.Vertical, - controller: SplitViewController( - limits: pans - .map( - (pan) => WeightLimit( - min: pan.minWeight, - max: pan.maxWeight, - ), - ) - .toList(growable: false), - ), - children: pans.map((pan) => pan.child).toList(growable: false), - ); - } -} diff --git a/learning/tour-of-beam/frontend/lib/main.dart b/learning/tour-of-beam/frontend/lib/main.dart index ffdb55c697d9..c7eb698c3782 100644 --- a/learning/tour-of-beam/frontend/lib/main.dart +++ b/learning/tour-of-beam/frontend/lib/main.dart @@ -17,6 +17,7 @@ */ import 'package:easy_localization/easy_localization.dart'; +import 'package:easy_localization_ext/easy_localization_ext.dart'; import 'package:easy_localization_loader/easy_localization_loader.dart'; import 'package:flutter/material.dart'; import 'package:playground_components/playground_components.dart'; @@ -38,7 +39,10 @@ void main() async { startLocale: englishLocale, fallbackLocale: englishLocale, path: 'assets/translations', - assetLoader: YamlAssetLoader(), + assetLoader: MultiAssetLoader([ + PlaygroundComponents.translationLoader, + YamlAssetLoader(), + ]), child: const TourOfBeamApp(), ), ); diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart b/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart new file mode 100644 index 000000000000..ed9dace49dc2 --- /dev/null +++ b/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:playground_components/playground_components.dart'; + +const String kApiClientURL = + 'https://backend-router-beta-dot-apache-beam-testing.appspot.com'; +const String kApiJavaClientURL = + 'https://backend-java-beta-dot-apache-beam-testing.appspot.com'; +const String kApiGoClientURL = + 'https://backend-go-beta-dot-apache-beam-testing.appspot.com'; +const String kApiPythonClientURL = + 'https://backend-python-beta-dot-apache-beam-testing.appspot.com'; +const String kApiScioClientURL = + 'https://backend-scio-beta-dot-apache-beam-testing.appspot.com'; + +class PlaygroundDemoWidget extends StatefulWidget { + const PlaygroundDemoWidget({Key? key}) : super(key: key); + + @override + State createState() => _PlaygroundDemoWidgetState(); +} + +class _PlaygroundDemoWidgetState extends State { + late final PlaygroundController playgroundController; + + @override + void initState() { + super.initState(); + + final exampleRepository = ExampleRepository( + client: GrpcExampleClient(url: kApiClientURL), + ); + + final codeRepository = CodeRepository( + client: GrpcCodeClient( + url: kApiClientURL, + runnerUrlsById: { + Sdk.java.id: kApiJavaClientURL, + Sdk.go.id: kApiGoClientURL, + Sdk.python.id: kApiPythonClientURL, + Sdk.scio.id: kApiScioClientURL, + }, + ), + ); + + final exampleCache = ExampleCache( + exampleRepository: exampleRepository, + hasCatalog: true, + ); + + playgroundController = PlaygroundController( + codeRepository: codeRepository, + exampleCache: exampleCache, + examplesLoader: ExamplesLoader(), + ); + + playgroundController.examplesLoader.load( + ExamplesLoadingDescriptor( + descriptors: [ + CatalogDefaultExampleLoadingDescriptor(sdk: Sdk.java), + ], + //initialSdk: Sdk.java, + ), + ); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: playgroundController, + builder: _buildOnChange, + ); + } + + Widget _buildOnChange(BuildContext context, Widget? child) { + final snippetController = playgroundController.snippetEditingController; + if (snippetController == null) { + return const LoadingIndicator(); + } + + return Stack( + children: [ + SplitView( + direction: Axis.vertical, + first: SnippetEditor( + controller: snippetController, + isEditable: true, + goToContextLine: false, + ), + second: OutputWidget( + playgroundController: playgroundController, + graphDirection: Axis.horizontal, + ), + ), + Positioned( + top: 30, + right: 30, + child: Row( + children: [ + RunOrCancelButton(playgroundController: playgroundController), + ], + ), + ), + ], + ); + } +} diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart b/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart index da6246f80afd..513a6879f729 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart @@ -24,10 +24,9 @@ import 'package:playground_components/playground_components.dart'; import '../../components/expansion_tile_wrapper.dart'; import '../../components/filler_text.dart'; import '../../components/scaffold.dart'; -import '../../components/split_view/pan.dart'; -import '../../components/split_view/widget.dart'; import '../../constants/sizes.dart'; import '../../generated/assets.gen.dart'; +import 'playground_demo.dart'; class TourScreen extends StatelessWidget { const TourScreen(); @@ -52,18 +51,10 @@ class _WideTour extends StatelessWidget { children: const [ _ContentTree(), Expanded( - child: TobSplitView( + child: SplitView( direction: Axis.horizontal, - pans: [ - Pan( - child: _Content(), - minWeight: 0.3, - ), - Pan( - child: _Playground(), - minWeight: 0.3, - ), - ], + first: _Content(), + second: PlaygroundDemoWidget(), ), ), ], @@ -89,7 +80,7 @@ class _NarrowTour extends StatelessWidget { DecoratedBox( decoration: BoxDecoration( border: Border( - top: BorderSide(color: ThemeColors.of(context).divider), + top: BorderSide(color: Theme.of(context).dividerColor), ), ), child: const _Playground(), @@ -283,14 +274,16 @@ class _Content extends StatelessWidget { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( height: MediaQuery.of(context).size.height - BeamSizes.appBarHeight - TobSizes.footerHeight, decoration: BoxDecoration( - color: ThemeColors.of(context).background, + color: themeData.backgroundColor, border: Border( - left: BorderSide(color: ThemeColors.of(context).divider), + left: BorderSide(color: themeData.dividerColor), ), ), child: Column( @@ -314,12 +307,14 @@ class _ContentFooter extends StatelessWidget { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( decoration: BoxDecoration( border: Border( - top: BorderSide(color: ThemeColors.of(context).divider), + top: BorderSide(color: themeData.dividerColor), ), - color: ThemeColors.of(context).secondaryBackground, + color: themeData.extension()?.secondaryBackgroundColor, ), width: double.infinity, padding: const EdgeInsets.all(BeamSizes.size20), @@ -329,8 +324,8 @@ class _ContentFooter extends StatelessWidget { Flexible( child: OutlinedButton( style: OutlinedButton.styleFrom( - foregroundColor: ThemeColors.of(context).primary, - side: BorderSide(color: ThemeColors.of(context).primary), + foregroundColor: themeData.primaryColor, + side: BorderSide(color: themeData.primaryColor), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(BeamSizes.size4), @@ -357,6 +352,7 @@ class _Playground extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center(child: Text('Playground')); + // TODO(alexeyinkin): Even this way the narrow layout breaks, https://github.com/apache/beam/issues/23244 + return const Center(child: Text('TODO: Playground for narrow screen')); } } diff --git a/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart b/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart index fdcf63e13591..8c295687cedf 100644 --- a/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart +++ b/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart @@ -90,7 +90,7 @@ class _SdkSelection extends StatelessWidget { BeamSizes.appBarHeight - TobSizes.footerHeight, ), - color: ThemeColors.of(context).background, + color: Theme.of(context).backgroundColor, child: Stack( children: [ Positioned( @@ -183,7 +183,7 @@ class _IntroText extends StatelessWidget { style: Theme.of(context) .textTheme .bodyLarge! - .copyWith(color: ThemeColors.of(context).primary), + .copyWith(color: Theme.of(context).primaryColor), recognizer: TapGestureRecognizer() ..onTap = () { // TODO(nausharipov): sign in @@ -250,7 +250,7 @@ class _SdkButton extends StatelessWidget { padding: const EdgeInsets.only(right: 15, bottom: 10), child: OutlinedButton( style: OutlinedButton.styleFrom( - backgroundColor: ThemeColors.of(context).background, + backgroundColor: Theme.of(context).backgroundColor, side: groupValue == value ? null : const BorderSide(color: BeamColors.grey1), @@ -336,12 +336,14 @@ class _ModuleBody extends StatelessWidget { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( margin: _moduleLeftMargin, decoration: BoxDecoration( border: Border( left: BorderSide( - color: ThemeColors.of(context).divider, + color: themeData.dividerColor, ), ), ), @@ -351,7 +353,7 @@ class _ModuleBody extends StatelessWidget { const FillerText(width: 20), const SizedBox(height: BeamSizes.size16), Divider( - color: ThemeColors.of(context).divider, + color: themeData.dividerColor, ), ], ), diff --git a/learning/tour-of-beam/frontend/pubspec.lock b/learning/tour-of-beam/frontend/pubspec.lock index 46d831721eca..85f5d587816b 100644 --- a/learning/tour-of-beam/frontend/pubspec.lock +++ b/learning/tour-of-beam/frontend/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "46.0.0" + aligned_dialog: + dependency: transitive + description: + name: aligned_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.6" analyzer: dependency: transitive description: @@ -127,6 +134,15 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.0" + code_text_field: + dependency: "direct main" + description: + path: "." + ref: "9e2c9fe52a69481f038f4b6609e8a0a776429437" + resolved-ref: "9e2c9fe52a69481f038f4b6609e8a0a776429437" + url: "https://github.com/BertrandBev/code_field.git" + source: git + version: "1.0.3" collection: dependency: transitive description: @@ -183,6 +199,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + easy_localization_ext: + dependency: "direct main" + description: + name: easy_localization_ext + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" easy_localization_loader: dependency: "direct main" description: @@ -197,6 +220,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.2" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -249,6 +279,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.3.0" + flutter_highlight: + dependency: transitive + description: + name: flutter_highlight + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" flutter_localizations: dependency: transitive description: flutter @@ -304,6 +341,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" graphs: dependency: transitive description: @@ -311,6 +355,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + grpc: + dependency: transitive + description: + name: grpc + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + highlight: + dependency: transitive + description: + name: highlight + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" http: dependency: transitive description: @@ -318,6 +376,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.13.4" + http2: + dependency: transitive + description: + name: http2 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" http_multi_server: dependency: transitive description: @@ -365,6 +430,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.6.0" + linked_scroll_controller: + dependency: transitive + description: + name: linked_scroll_controller + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" logging: dependency: transitive description: @@ -526,6 +598,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" provider: dependency: "direct main" description: @@ -629,13 +708,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" - split_view: - dependency: "direct main" - description: - name: split_view - url: "https://pub.dartlang.org" - source: hosted - version: "3.2.1" stack_trace: dependency: transitive description: @@ -841,4 +913,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.17.6 <3.0.0" - flutter: ">=3.0.0" + flutter: ">=3.3.1" diff --git a/learning/tour-of-beam/frontend/pubspec.yaml b/learning/tour-of-beam/frontend/pubspec.yaml index c11c3f0be732..547d378d2f93 100644 --- a/learning/tour-of-beam/frontend/pubspec.yaml +++ b/learning/tour-of-beam/frontend/pubspec.yaml @@ -18,26 +18,29 @@ name: tour_of_beam description: Tour of Beam -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' version: 0.1.0 environment: sdk: ">=2.17.6 <3.0.0" - flutter: ">=3.0.0 <4.0.0" + flutter: ">=3.3.1" dependencies: + code_text_field: + git: + url: https://github.com/BertrandBev/code_field.git + ref: 9e2c9fe52a69481f038f4b6609e8a0a776429437 easy_localization: ^3.0.1 + easy_localization_ext: ^0.1.0 easy_localization_loader: ^1.0.0 flutter: { sdk: flutter } flutter_svg: ^1.0.3 get_it: ^7.2.0 google_fonts: ^3.0.1 - playground_components: - path: ../../../playground/frontend/playground_components + playground_components: { path: ../../../playground/frontend/playground_components } provider: ^6.0.3 shared_preferences: ^2.0.15 - split_view: ^3.2.1 url_launcher: ^6.1.5 url_strategy: ^0.2.0 diff --git a/playground/buf.gen.yaml b/playground/buf.gen.yaml index bd9b9f3d8309..d04b54a6c5dd 100644 --- a/playground/buf.gen.yaml +++ b/playground/buf.gen.yaml @@ -28,5 +28,5 @@ plugins: - paths=source_relative - require_unimplemented_servers=false - name: dart - out: frontend/lib - opt: grpc \ No newline at end of file + out: frontend/playground_components/lib/src + opt: grpc diff --git a/playground/frontend/assets/theme.svg b/playground/frontend/assets/theme.svg deleted file mode 100644 index 10d8b3d5c9be..000000000000 --- a/playground/frontend/assets/theme.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - diff --git a/playground/frontend/assets/translations/en.yaml b/playground/frontend/assets/translations/en.yaml new file mode 100644 index 000000000000..c7d74f96d44f --- /dev/null +++ b/playground/frontend/assets/translations/en.yaml @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +intents: + playground: + clearOutput: 'Clear Output' + newExample: 'New Example' diff --git a/playground/frontend/lib/components/banner/banner_description.dart b/playground/frontend/lib/components/banner/banner_description.dart index ab9ccfd51793..34ff3b51cefa 100644 --- a/playground/frontend/lib/components/banner/banner_description.dart +++ b/playground/frontend/lib/components/banner/banner_description.dart @@ -18,7 +18,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/description_popover/description_popover.dart'; @@ -61,10 +60,9 @@ class BannerDescription extends StatelessWidget { RichText( text: TextSpan( children: [ - TextSpan( + const TextSpan( text: kBannerDescription1, style: TextStyle( - color: ThemeColors.of(context).textColor, height: kDescriptionLineHeight, ), ), @@ -76,10 +74,9 @@ class BannerDescription extends StatelessWidget { ..onTap = () async { launchUrl(Uri.parse(kBannerUrl)); }), - TextSpan( + const TextSpan( text: kHyperlinkText, style: TextStyle( - color: ThemeColors.of(context).textColor, height: kDescriptionLineHeight, ), ), diff --git a/playground/frontend/lib/components/dropdown_button/dropdown_button.dart b/playground/frontend/lib/components/dropdown_button/dropdown_button.dart index fa89fce72ff0..c2aff6f2f950 100644 --- a/playground/frontend/lib/components/dropdown_button/dropdown_button.dart +++ b/playground/frontend/lib/components/dropdown_button/dropdown_button.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/utils/dropdown_utils.dart'; +import 'package:playground_components/playground_components.dart'; const int kAnimationDurationInMilliseconds = 80; const Offset kAnimationBeginOffset = Offset(0.0, -0.02); @@ -87,10 +87,12 @@ class _AppDropdownButtonState extends State @override Widget build(BuildContext context) { + final ext = Theme.of(context).extension()!; + return Container( height: kContainerHeight, decoration: BoxDecoration( - color: ThemeColors.of(context).dropdownButton, + color: ext.fieldBackgroundColor, borderRadius: BorderRadius.circular(kSmBorderRadius), ), child: TextButton( @@ -149,7 +151,7 @@ class _AppDropdownButtonState extends State height: widget.height, width: widget.width, decoration: BoxDecoration( - color: ThemeColors.of(context).background, + color: Theme.of(context).backgroundColor, borderRadius: BorderRadius.circular(kMdBorderRadius), ), child: widget.createDropdown(_close), diff --git a/playground/frontend/lib/components/playground_run_or_cancel_button.dart b/playground/frontend/lib/components/playground_run_or_cancel_button.dart new file mode 100644 index 000000000000..b555a71a882c --- /dev/null +++ b/playground/frontend/lib/components/playground_run_or_cancel_button.dart @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/widgets.dart'; +import 'package:playground_components/playground_components.dart'; +import 'package:provider/provider.dart'; + +import '../modules/analytics/analytics_service.dart'; +import '../utils/analytics_utils.dart'; + +class PlaygroundRunOrCancelButton extends StatelessWidget { + const PlaygroundRunOrCancelButton(); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, playgroundController, child) { + final analyticsService = AnalyticsService.get(context); + final stopwatch = Stopwatch(); + final exampleName = getAnalyticsExampleName(playgroundController); + + return RunOrCancelButton( + playgroundController: playgroundController, + beforeCancel: () { + final exampleName = getAnalyticsExampleName(playgroundController); + analyticsService.trackClickCancelRunEvent(exampleName); + }, + beforeRun: () { + stopwatch.start(); + analyticsService.trackClickRunEvent(exampleName); + }, + onComplete: () { + analyticsService.trackRunTimeEvent( + exampleName, + stopwatch.elapsedMilliseconds, + ); + }, + ); + } + ); + } +} diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart b/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart deleted file mode 100644 index 730b5b240b92..000000000000 --- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_button.dart +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/assets.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:provider/provider.dart'; - -class ToggleThemeButton extends StatelessWidget { - const ToggleThemeButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final appLocale = AppLocalizations.of(context)!; - - return Consumer(builder: (context, notifier, child) { - final text = notifier.isDarkMode ? appLocale.lightMode : appLocale.darkMode; - - return Padding( - padding: const EdgeInsets.symmetric( - vertical: kSmSpacing, - horizontal: kMdSpacing, - ), - child: TextButton.icon( - icon: SvgPicture.asset(kThemeIconAsset), - label: Text(text), - onPressed: () { - notifier.toggleTheme(); - AnalyticsService.get(context) - .trackClickToggleTheme(!notifier.isDarkMode); - }, - ), - ); - }); - } -} diff --git a/playground/frontend/lib/config/theme.dart b/playground/frontend/lib/config/theme.dart deleted file mode 100644 index 62a30f0082b0..000000000000 --- a/playground/frontend/lib/config/theme.dart +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:code_text_field/code_text_field.dart'; -import 'package:flutter/material.dart'; -import 'package:playground/constants/colors.dart'; -import 'package:playground/constants/font_weight.dart'; -import 'package:playground/constants/fonts.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/editor/components/editor_themes.dart'; -import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -const kThemeMode = 'theme_mode'; - -class ThemeSwitchNotifier extends ChangeNotifier { - late SharedPreferences _preferences; - ThemeMode themeMode = ThemeMode.light; - - static const _darkThemeColors = ThemeColors.fromBrightness(isDark: true); - static const _lightThemeColors = ThemeColors.fromBrightness(isDark: false); - - ThemeColors get themeColors { - switch (themeMode) { - case ThemeMode.dark: - return _darkThemeColors; - default: - return _lightThemeColors; - } - } - - final _darkCodeTheme = createTheme(_darkThemeColors); - final _lightCodeTheme = createTheme(_lightThemeColors); - - CodeThemeData get codeTheme { - switch (themeMode) { - case ThemeMode.dark: - return _darkCodeTheme; - default: - return _lightCodeTheme; - } - } - - init() { - _setPreferences(); - } - - _setPreferences() async { - _preferences = await SharedPreferences.getInstance(); - themeMode = _preferences.getString(kThemeMode) == ThemeMode.dark.toString() - ? ThemeMode.dark - : ThemeMode.light; - notifyListeners(); - } - - bool get isDarkMode { - return themeMode == ThemeMode.dark; - } - - void toggleTheme() { - themeMode = themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light; - _preferences.setString(kThemeMode, themeMode.toString()); - notifyListeners(); - } -} - -class ThemeSwitchNotifierProvider extends StatelessWidget { - final Widget child; - - const ThemeSwitchNotifierProvider({ - super.key, - required this.child, - }); - - @override - Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (context) => ThemeSwitchNotifier()..init(), - child: Consumer( - builder: (context, themeSwitchNotifier, _) => ThemeColorsProvider( - data: themeSwitchNotifier.themeColors, - child: child, - ), - ), - ); - } -} - -class ThemeColorsProvider extends StatelessWidget { - final ThemeColors data; - final Widget child; - - const ThemeColorsProvider({ - super.key, - required this.data, - required this.child, - }); - - @override - Widget build(BuildContext context) { - return Provider.value( - value: data, - child: child, - ); - } -} - -TextTheme createTextTheme(Color textColor) { - return getBaseFontTheme( - const TextTheme( - headline1: TextStyle(), - headline2: TextStyle(), - headline3: TextStyle(), - headline4: TextStyle(), - headline5: TextStyle(), - headline6: TextStyle(), - subtitle1: TextStyle(), - subtitle2: TextStyle(), - bodyText1: TextStyle(), - bodyText2: TextStyle(), - caption: TextStyle(), - overline: TextStyle(), - button: TextStyle(fontWeight: kBoldWeight), - ).apply( - bodyColor: textColor, - displayColor: textColor, - ), - ); -} - -TextButtonThemeData createTextButtonTheme(Color textColor) { - return TextButtonThemeData( - style: TextButton.styleFrom( - primary: textColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(kLgBorderRadius)), - ), - ), - ); -} - -OutlinedButtonThemeData createOutlineButtonTheme(Color textColor) { - return OutlinedButtonThemeData( - style: OutlinedButton.styleFrom( - primary: textColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(kSmBorderRadius)), - ), - ), - ); -} - -ElevatedButtonThemeData createElevatedButtonTheme(Color primaryColor) { - return ElevatedButtonThemeData( - style: ElevatedButton.styleFrom(primary: primaryColor), - ); -} - -PopupMenuThemeData createPopupMenuTheme() { - return const PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(kLgBorderRadius), - ), - ), - ); -} - -AppBarTheme createAppBarTheme(Color backgroundColor) { - return AppBarTheme( - color: backgroundColor, - elevation: 1, - centerTitle: false, - ); -} - -TabBarTheme createTabBarTheme(Color textColor, Color indicatorColor) { - const labelStyle = TextStyle(fontWeight: kMediumWeight); - return TabBarTheme( - unselectedLabelColor: textColor, - labelColor: textColor, - labelStyle: labelStyle, - unselectedLabelStyle: labelStyle, - indicator: UnderlineTabIndicator( - borderSide: BorderSide(width: 2.0, color: indicatorColor), - ), - ); -} - -DialogTheme createDialogTheme(Color textColor) { - return DialogTheme( - titleTextStyle: TextStyle( - color: textColor, - fontSize: 32.0, - fontWeight: kBoldWeight, - ), - ); -} - -final kLightTheme = ThemeData( - brightness: Brightness.light, - primaryColor: kLightPrimary, - backgroundColor: kLightPrimaryBackground, - appBarTheme: createAppBarTheme(kLightSecondaryBackground), - textTheme: createTextTheme(kLightText), - popupMenuTheme: createPopupMenuTheme(), - textButtonTheme: createTextButtonTheme(kLightText), - outlinedButtonTheme: createOutlineButtonTheme(kLightText), - elevatedButtonTheme: createElevatedButtonTheme(kLightPrimary), - tabBarTheme: createTabBarTheme(kLightText, kLightPrimary), - dialogTheme: createDialogTheme(kLightText), -); - -final kDarkTheme = ThemeData( - brightness: Brightness.dark, - primaryColor: kDarkPrimary, - backgroundColor: kDarkPrimaryBackground, - appBarTheme: createAppBarTheme(kDarkSecondaryBackground), - textTheme: createTextTheme(kDarkText), - popupMenuTheme: createPopupMenuTheme(), - textButtonTheme: createTextButtonTheme(kDarkText), - outlinedButtonTheme: createOutlineButtonTheme(kDarkText), - elevatedButtonTheme: createElevatedButtonTheme(kDarkPrimary), - tabBarTheme: createTabBarTheme(kDarkText, kDarkPrimary), - dialogTheme: createDialogTheme(kDarkText), -); - -class ThemeColors { - final Color? _background; - final Color? _dropdownButton; - - final bool isDark; - - static ThemeColors of(BuildContext context, {bool listen = true}) { - return Provider.of(context, listen: listen); - } - - ThemeColors({ - required this.isDark, - Color? background, - Color? dropdownButtonColor, - }) : _background = background, - _dropdownButton = dropdownButtonColor; - - const ThemeColors.fromBrightness({ - required this.isDark, - }) : _background = null, - _dropdownButton = null; - - ThemeColors copyWith({ - Color? background, - Color? dropdownButton, - }) { - return ThemeColors( - isDark: isDark, - background: background ?? this.background, - dropdownButtonColor: dropdownButton ?? this.dropdownButton, - ); - } - - Color get dropdownButton => - _dropdownButton ?? (isDark ? kDarkGrey : kLightGrey); - - Color get divider => isDark ? kDarkGrey : kLightGrey; - - Color get lightGreyColor => isDark ? kLightGrey1 : kLightGrey; - - Color get primary => isDark ? kLightPrimary : kDarkPrimary; - - Color get primaryBackgroundTextColor => Colors.white; - - Color get lightGreyBackgroundTextColor => Colors.black; - - Color get grey1Color => isDark ? kDarkGrey1 : kLightGrey1; - - Color get secondaryBackground => - isDark ? kDarkSecondaryBackground : kLightSecondaryBackground; - - Color get background => - _background ?? - (isDark ? kDarkPrimaryBackground : kLightPrimaryBackground); - - Color get code1 => isDark ? kDarkCode2 : kLightCode2; - - Color get code2 => isDark ? kDarkCode1 : kLightCode1; - - Color get codeComment => isDark ? kDarkCodeComment : kLightCodeComment; - - Color get textColor => isDark ? kDarkText : kLightText; -} diff --git a/playground/frontend/lib/configure_nonweb.dart b/playground/frontend/lib/configure_nonweb.dart deleted file mode 100644 index 4568ded62f94..000000000000 --- a/playground/frontend/lib/configure_nonweb.dart +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -void configureApp() { - // see https://flutter.dev/docs/development/ui/navigation/url-strategies -} diff --git a/playground/frontend/lib/constants/sizes.dart b/playground/frontend/lib/constants/sizes.dart index 3d895aae285d..c4515a5e3dc1 100644 --- a/playground/frontend/lib/constants/sizes.dart +++ b/playground/frontend/lib/constants/sizes.dart @@ -25,23 +25,17 @@ const double kXlSpacing = 16.0; const double kXxlSpacing = 36.0; // sizes -const kHeaderButtonHeight = 46.0; -const kRunButtonWidth = 150.0; const kButtonHeight = 40.0; const kIconButtonSplashRadius = 24.0; -const kFooterHeight = 32.0; // border radius const double kSmBorderRadius = 4.0; const double kMdBorderRadius = 6.0; -const double kLgBorderRadius = 8.0; -const double kXlBorderRadius = 28.0; // elevation const double kElevation = 2; // icon sizes -const double kIconSizeXs = 8.0; const double kIconSizeSm = 16.0; const double kIconSizeMd = 24.0; const double kIconSizeLg = 32.0; @@ -52,16 +46,8 @@ const double kCursorSize = 1.0; // container size const double kContainerHeight = 40.0; -const double kCaptionFontSize = 10.0; -const double kCodeFontSize = 14.0; const double kLabelFontSize = 16.0; -const double kHintFontSize = 16.0; const double kTitleFontSize = 18.0; //divider size const double kDividerHeight = 1.0; -const double kLgDividerHeight = 2.0; - -//loading indicator size -const double kMdLoadingIndicatorSize = 40.0; -const double kLgLoadingIndicatorSize = 50.0; diff --git a/playground/frontend/lib/l10n/app_en.arb b/playground/frontend/lib/l10n/app_en.arb index 48a3a8675b82..538df437994a 100644 --- a/playground/frontend/lib/l10n/app_en.arb +++ b/playground/frontend/lib/l10n/app_en.arb @@ -7,14 +7,6 @@ "@darkMode": { "description": "Title for a theme switch" }, - "newExample": "New Example", - "@newExample": { - "description": "Title for the New Example button" - }, - "reset": "Reset", - "@reset": { - "description": "Title for the reset button" - }, "run": "Run", "@run": { "description": "Title for the run button" @@ -143,10 +135,6 @@ "@reportIssue": { "description": "Title for the Report issue in GitHub button" }, - "codeTextArea": "Code Text Area", - "@codeTextArea": { - "description": "Title for the Code text area semantics" - }, "bottom": "Bottom", "@bottom": { "description": "Part of the output placements semantics label" @@ -159,10 +147,6 @@ "@left": { "description": "Part of the output placements semantics label" }, - "clearOutput": "Clear Output", - "@clearOutput": { - "description": "Title for the Clear Output shortcut row" - }, "pipelineOptions": "Pipeline Options", "@pipelineOptions": { "description": "Title for the Pipeline Options" diff --git a/playground/frontend/lib/l10n/l10n.dart b/playground/frontend/lib/l10n/l10n.dart index cd63ec779909..9f7e197317e4 100644 --- a/playground/frontend/lib/l10n/l10n.dart +++ b/playground/frontend/lib/l10n/l10n.dart @@ -16,10 +16,12 @@ * limitations under the License. */ -import 'package:flutter/material.dart'; +import 'dart:ui'; class L10n { + static const en = Locale('en'); + static const locales = [ - Locale('en'), + en, ]; } diff --git a/playground/frontend/lib/main.dart b/playground/frontend/lib/main.dart index 2e57fc49a120..0c36af3e7bcf 100644 --- a/playground/frontend/lib/main.dart +++ b/playground/frontend/lib/main.dart @@ -17,15 +17,34 @@ */ import 'package:akvelon_flutter_issue_106664_workaround/akvelon_flutter_issue_106664_workaround.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:easy_localization_ext/easy_localization_ext.dart'; +import 'package:easy_localization_loader/easy_localization_loader.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl_browser.dart'; -import 'package:playground/configure_nonweb.dart' -if (dart.library.html) 'package:playground/configure_web.dart'; import 'package:playground/playground_app.dart'; +import 'package:playground_components/playground_components.dart'; +import 'package:url_strategy/url_strategy.dart'; -void main() { +import 'l10n/l10n.dart'; + +void main() async { FlutterIssue106664Workaround.instance.apply(); - findSystemLocale(); - configureApp(); - runApp(const PlaygroundApp()); + setPathUrlStrategy(); + await EasyLocalization.ensureInitialized(); + + await findSystemLocale(); + runApp( + EasyLocalization( + supportedLocales: L10n.locales, + startLocale: L10n.en, + fallbackLocale: L10n.en, + path: 'assets/translations', + assetLoader: MultiAssetLoader([ + PlaygroundComponents.translationLoader, + YamlAssetLoader(), + ]), + child: const PlaygroundApp(), + ), + ); } diff --git a/playground/frontend/lib/modules/actions/components/new_example_action.dart b/playground/frontend/lib/modules/actions/components/new_example_action.dart index 332b4beba397..594492987c11 100644 --- a/playground/frontend/lib/modules/actions/components/new_example_action.dart +++ b/playground/frontend/lib/modules/actions/components/new_example_action.dart @@ -16,13 +16,11 @@ * limitations under the License. */ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/modules/actions/components/header_icon_button.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:playground/modules/shortcuts/components/shortcut_tooltip.dart'; import 'package:playground/modules/shortcuts/constants/global_shortcuts.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:url_launcher/url_launcher.dart'; class NewExampleAction extends StatelessWidget { @@ -35,9 +33,9 @@ class NewExampleAction extends StatelessWidget { child: HeaderIconButton( icon: Icon( Icons.add_circle_outline, - color: ThemeColors.of(context).grey1Color, + color: Theme.of(context).extension()?.iconColor, ), - label: AppLocalizations.of(context)!.newExample, + label: 'intents.playground.newExample'.tr(), onPressed: () { launchUrl(Uri.parse('/')); AnalyticsService.get(context).trackClickNewExample(); diff --git a/playground/frontend/lib/modules/actions/components/reset_action.dart b/playground/frontend/lib/modules/actions/components/reset_action.dart index 533bdec2d1c4..a0e111996322 100644 --- a/playground/frontend/lib/modules/actions/components/reset_action.dart +++ b/playground/frontend/lib/modules/actions/components/reset_action.dart @@ -17,33 +17,21 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/assets.dart'; -import 'package:playground/modules/actions/components/header_icon_button.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:playground/modules/shortcuts/components/shortcut_tooltip.dart'; -import 'package:playground/modules/shortcuts/constants/global_shortcuts.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:playground_components/playground_components.dart'; +import 'package:provider/provider.dart'; class ResetAction extends StatelessWidget { - final VoidCallback reset; - - const ResetAction({Key? key, required this.reset}) : super(key: key); + const ResetAction(); @override Widget build(BuildContext context) { - return ShortcutTooltip( - shortcut: kResetShortcut, - child: HeaderIconButton( - icon: SvgPicture.asset( - kResetIconAsset, - color: ThemeColors.of(context).grey1Color, - ), - label: AppLocalizations.of(context)!.reset, - onPressed: () { - reset(); - AnalyticsService.get(context).trackReset(); + final analyticsService = AnalyticsService.get(context); + return Consumer( + builder: (context, playgroundController, child) => ResetButton( + playgroundController: playgroundController, + beforeReset: () { + analyticsService.trackReset(); }, ), ); diff --git a/playground/frontend/lib/modules/analytics/analytics_service.dart b/playground/frontend/lib/modules/analytics/analytics_service.dart index 6571a4898d44..0416e7dcc520 100644 --- a/playground/frontend/lib/modules/analytics/analytics_service.dart +++ b/playground/frontend/lib/modules/analytics/analytics_service.dart @@ -17,8 +17,7 @@ */ import 'package:flutter/widgets.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; abstract class AnalyticsService { @@ -26,8 +25,8 @@ abstract class AnalyticsService { return Provider.of(context, listen: false); } - void trackSelectSdk(SDK? oldSdk, SDK newSdk); - void trackSelectExample(ExampleModel newExample); + void trackSelectSdk(Sdk? oldSdk, Sdk newSdk); + void trackSelectExample(ExampleBase newExample); void trackClickNewExample(); void trackReset(); void trackClickToggleTheme(bool isDark); diff --git a/playground/frontend/lib/modules/analytics/google_analytics_service.dart b/playground/frontend/lib/modules/analytics/google_analytics_service.dart index d9eaf2e6f120..7b083b3bc25e 100644 --- a/playground/frontend/lib/modules/analytics/google_analytics_service.dart +++ b/playground/frontend/lib/modules/analytics/google_analytics_service.dart @@ -19,24 +19,23 @@ import 'package:playground/config.g.dart'; import 'package:playground/modules/analytics/analytics_events.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:usage/usage_html.dart'; class GoogleAnalyticsService implements AnalyticsService { final _analytics = AnalyticsHtml(kAnalyticsUA, 'beam', '1.0'); @override - void trackSelectSdk(SDK? oldSdk, SDK newSdk) { + void trackSelectSdk(Sdk? oldSdk, Sdk newSdk) { safeSendEvent( kSdkCategory, kSelectSdkEvent, - label: '${oldSdk?.displayName}_${newSdk.displayName}', + label: '${oldSdk?.title}_${newSdk.title}', ); } @override - void trackSelectExample(ExampleModel newExample) { + void trackSelectExample(ExampleBase newExample) { safeSendEvent( kExampleCategory, kSelectExampleEvent, diff --git a/playground/frontend/lib/modules/editor/components/editor_themes.dart b/playground/frontend/lib/modules/editor/components/editor_themes.dart deleted file mode 100644 index 42c68905f962..000000000000 --- a/playground/frontend/lib/modules/editor/components/editor_themes.dart +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:code_text_field/code_text_field.dart'; -import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; - -CodeThemeData createTheme(ThemeColors colors) { - return CodeThemeData( - styles: _createThemeStyles(colors), - ); -} - -Map _createThemeStyles(ThemeColors colors) { - return { - 'root': TextStyle( - backgroundColor: colors.background, - color: colors.textColor, - ), - 'comment': TextStyle(color: colors.codeComment), - 'quote': TextStyle(color: colors.code2), - 'variable': TextStyle(color: colors.code2), - 'keyword': TextStyle(color: colors.code2), - 'selector-tag': TextStyle(color: colors.code2), - 'built_in': TextStyle(color: colors.code2), - 'name': TextStyle(color: colors.code2), - 'tag': TextStyle(color: colors.code2), - 'string': TextStyle(color: colors.code1), - 'title': TextStyle(color: colors.code1), - 'section': TextStyle(color: colors.code1), - 'attribute': TextStyle(color: colors.code1), - 'literal': TextStyle(color: colors.code1), - 'template-tag': TextStyle(color: colors.code1), - 'template-variable': TextStyle(color: colors.code1), - 'type': TextStyle(color: colors.code1), - 'addition': TextStyle(color: colors.code1), - 'deletion': TextStyle(color: colors.code2), - 'selector-attr': TextStyle(color: colors.code2), - 'selector-pseudo': TextStyle(color: colors.code2), - 'meta': TextStyle(color: colors.code2), - 'doctag': TextStyle(color: colors.codeComment), - 'attr': TextStyle(color: colors.primary), - 'symbol': TextStyle(color: colors.code2), - 'bullet': TextStyle(color: colors.code2), - 'link': TextStyle(color: colors.code2), - 'emphasis': const TextStyle(fontStyle: FontStyle.italic), - 'strong': const TextStyle(fontWeight: FontWeight.bold), - }; -} diff --git a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_body.dart b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_body.dart index fd1d6c9eedaa..0b8cd5836b4b 100644 --- a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_body.dart +++ b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_body.dart @@ -24,7 +24,7 @@ import 'package:playground/modules/editor/components/pipeline_options_dropdown/p import 'package:playground/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_input.dart'; import 'package:playground/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_separator.dart'; import 'package:playground/modules/editor/components/pipeline_options_dropdown/pipeline_options_form.dart'; -import 'package:playground/modules/editor/parsers/run_options_parser.dart'; +import 'package:playground_components/playground_components.dart'; const kOptionsTabIndex = 0; const kRawTabIndex = 1; diff --git a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_separator.dart b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_separator.dart index 17070dd84f16..78f8b0d7c34c 100644 --- a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_separator.dart +++ b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_dropdown_separator.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; +import 'package:playground_components/playground_components.dart'; class PipelineOptionsDropdownSeparator extends StatelessWidget { const PipelineOptionsDropdownSeparator({Key? key}) : super(key: key); @@ -28,7 +28,7 @@ class PipelineOptionsDropdownSeparator extends StatelessWidget { return Container( height: kDividerHeight, decoration: BoxDecoration( - color: ThemeColors.of(context).lightGreyColor, + color: Theme.of(context).extension()?.borderColor, ), ); } diff --git a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_form.dart b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_form.dart index a7f2eabf87e3..6aa41c0c3cdf 100644 --- a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_form.dart +++ b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_form.dart @@ -16,10 +16,9 @@ * limitations under the License. */ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:collection/collection.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/colors.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/editor/components/pipeline_options_dropdown/pipeline_option_controller.dart'; @@ -81,7 +80,7 @@ class PipelineOptionsForm extends StatelessWidget { Icons.delete_outlined, color: kLightPrimary, ), - color: ThemeColors.of(context).grey1Color, + color: Theme.of(context).dividerColor, onPressed: () => onDelete(index), ), ), diff --git a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_text_field.dart b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_text_field.dart index 2c1187365288..d4202b125697 100644 --- a/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_text_field.dart +++ b/playground/frontend/lib/modules/editor/components/pipeline_options_dropdown/pipeline_options_text_field.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; +import 'package:playground_components/playground_components.dart'; class PipelineOptionsTextField extends StatelessWidget { final TextEditingController controller; @@ -32,6 +32,9 @@ class PipelineOptionsTextField extends StatelessWidget { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + final ext = themeData.extension()!; + return Container( margin: const EdgeInsets.only( top: kMdSpacing, @@ -48,16 +51,15 @@ class PipelineOptionsTextField extends StatelessWidget { controller: controller, decoration: InputDecoration( contentPadding: const EdgeInsets.all(kMdSpacing), - border: _getInputBorder(ThemeColors.of(context).lightGreyColor), - focusedBorder: _getInputBorder(ThemeColors.of(context).primary), + border: _getInputBorder(ext.borderColor), + focusedBorder: _getInputBorder(themeData.primaryColor), ), - cursorColor: ThemeColors.of(context).textColor, ), ), ); } - _getInputBorder(Color color) { + OutlineInputBorder _getInputBorder(Color color) { return OutlineInputBorder( borderSide: BorderSide(color: color), borderRadius: BorderRadius.circular(kMdBorderRadius), diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart index 9708a88208a0..67430514e5a3 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/link_text_field.dart @@ -18,9 +18,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; +import 'package:playground_components/playground_components.dart'; const _kTextFieldMaxHeight = 45.0; @@ -45,9 +45,11 @@ class _LinkTextFieldState extends State { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( decoration: BoxDecoration( - color: ThemeColors.of(context).dropdownButton, + color: themeData.extension()?.borderColor, borderRadius: BorderRadius.circular(kSmBorderRadius), ), child: Container( @@ -66,7 +68,7 @@ class _LinkTextFieldState extends State { style: TextStyle( fontSize: kLabelFontSize, fontWeight: kNormalWeight, - color: ThemeColors.of(context).primary, + color: themeData.primaryColor, ), ), ), diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart index 9355934e7186..f0f6026ea693 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_button.dart @@ -19,8 +19,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/components/dropdown_button/dropdown_button.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_dropdown_body.dart'; +import 'package:playground_components/playground_components.dart'; const _kShareDropdownHeight = 140.0; const _kShareDropdownWidth = 460.0; @@ -31,22 +31,28 @@ class ShareButton extends StatelessWidget { @override Widget build(BuildContext context) { + final parentThemeData = Theme.of(context); + final ext = parentThemeData.extension()!; final appLocale = AppLocalizations.of(context)!; - final parentThemeData = ThemeColors.of(context); final themeData = parentThemeData.copyWith( - background: parentThemeData.secondaryBackground, - dropdownButton: parentThemeData.primary.withOpacity(_kButtonColorOpacity), + backgroundColor: ext.secondaryBackgroundColor, + extensions: { + ext.copyWith( + fieldBackgroundColor: + parentThemeData.primaryColor.withOpacity(_kButtonColorOpacity), + ), + }, ); - return ThemeColorsProvider( + return Theme( data: themeData, child: AppDropdownButton( buttonText: Text(appLocale.shareMyCode), showArrow: false, leading: Icon( Icons.share_outlined, - color: ThemeColors.of(context).primary, + color: themeData.primaryColor, ), height: _kShareDropdownHeight, width: _kShareDropdownWidth, diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart index 68a208373671..18366335fee7 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_dropdown_body.dart @@ -19,7 +19,7 @@ import 'package:flutter/material.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_tabs_headers.dart'; -import 'package:playground/modules/output/components/output_header/tab_header.dart'; +import 'package:playground_components/playground_components.dart'; const _kTabsCount = 2; diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart index 8c2e72948ae0..45d5b82cd005 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/share_tabs.dart @@ -19,7 +19,7 @@ import 'package:flutter/material.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class ShareTabs extends StatelessWidget { @@ -34,17 +34,17 @@ class ShareTabs extends StatelessWidget { Widget build(BuildContext context) { return Container( color: Theme.of(context).backgroundColor, - child: Consumer( - builder: (context, playgroundState, _) { - if (playgroundState.isExampleChanged) { + child: Consumer( + builder: (context, playgroundController, _) { + if (playgroundController.isExampleChanged) { return SnippetSaveAndShareTabs( - playgroundState: playgroundState, + playgroundController: playgroundController, tabController: tabController, ); } return ExampleShareTabs( - examplePath: playgroundState.selectedExample!.path, + examplePath: playgroundController.selectedExample!.path, tabController: tabController, ); }, diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart index ddd1bbb0f536..050725de8d92 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs/snippet_save_and_share_tabs.dart @@ -17,28 +17,26 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/components/loading_indicator/loading_indicator.dart'; -import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_tabs/example_share_tabs.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; class SnippetSaveAndShareTabs extends StatelessWidget { - final PlaygroundState playgroundState; + final PlaygroundController playgroundController; final TabController tabController; const SnippetSaveAndShareTabs({ super.key, - required this.playgroundState, + required this.playgroundController, required this.tabController, }); @override Widget build(BuildContext context) { return FutureBuilder( - future: playgroundState.getSnippetId(), + future: playgroundController.getSnippetId(), builder: (context, snapshot) { if (!snapshot.hasData) { - return const LoadingIndicator(size: kLgLoadingIndicatorSize); + return const LoadingIndicator(); } return ExampleShareTabs( diff --git a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart index 4d80b4309bd2..1d528a0b228e 100644 --- a/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart +++ b/playground/frontend/lib/modules/editor/components/share_dropdown/share_tabs_headers.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -35,17 +35,19 @@ class ShareTabsHeaders extends StatelessWidget { Widget build(BuildContext context) { final appLocale = AppLocalizations.of(context)!; - return Consumer(builder: (context, state, child) { - return SizedBox( - width: _width, - child: TabBar( - controller: tabController, - tabs: [ - Text(appLocale.link), - Text(appLocale.embed), - ], - ), - ); - }); + return Consumer( + builder: (context, controller, child) { + return SizedBox( + width: _width, + child: TabBar( + controller: tabController, + tabs: [ + Text(appLocale.link), + Text(appLocale.embed), + ], + ), + ); + }, + ); } } diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart b/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart deleted file mode 100644 index 6ec86f1826b2..000000000000 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:grpc/grpc_web.dart'; -import 'package:playground/api/iis_workaround_channel.dart'; -import 'package:playground/api/v1/api.pbgrpc.dart' as grpc; -import 'package:playground/config.g.dart'; -import 'package:playground/modules/editor/parsers/run_options_parser.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/run_code_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_error.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/utils/replace_incorrect_symbols.dart'; - -const kGeneralError = 'Failed to execute code'; - -class GrpcCodeClient implements CodeClient { - late final grpc.PlaygroundServiceClient _defaultClient; - - GrpcCodeClient() { - final channel = IisWorkaroundChannel.xhr( - Uri.parse(kApiClientURL), - ); - _defaultClient = grpc.PlaygroundServiceClient(channel); - } - - @override - Future runCode(RunCodeRequestWrapper request) { - return _runSafely(() => _createRunCodeClient(request.sdk) - .runCode(_toGrpcRequest(request)) - .then((response) => RunCodeResponse(response.pipelineUuid))); - } - - @override - Future cancelExecution(String pipelineUuid) { - return _runSafely(() => - _defaultClient.cancel(grpc.CancelRequest(pipelineUuid: pipelineUuid))); - } - - @override - Future checkStatus( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .checkStatus(grpc.CheckStatusRequest(pipelineUuid: pipelineUuid)) - .then( - (response) => CheckStatusResponse(_toClientStatus(response.status)), - )); - } - - @override - Future getCompileOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getCompileOutput( - grpc.GetCompileOutputRequest(pipelineUuid: pipelineUuid), - ) - .then((response) => _toOutputResponse(response.output))); - } - - @override - Future getRunOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getRunOutput(grpc.GetRunOutputRequest(pipelineUuid: pipelineUuid)) - .then((response) => _toOutputResponse(response.output)) - .catchError((err) { - print(err); - return _toOutputResponse(''); - })); - } - - @override - Future getLogOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getLogs(grpc.GetLogsRequest(pipelineUuid: pipelineUuid)) - .then((response) => _toOutputResponse(response.output)) - .catchError((err) { - print(err); - return _toOutputResponse(''); - })); - } - - @override - Future getRunErrorOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getRunError(grpc.GetRunErrorRequest(pipelineUuid: pipelineUuid)) - .then((response) => _toOutputResponse(response.output))); - } - - @override - Future getValidationErrorOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getValidationOutput( - grpc.GetValidationOutputRequest(pipelineUuid: pipelineUuid)) - .then((response) => _toOutputResponse(response.output))); - } - - @override - Future getPreparationErrorOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getPreparationOutput( - grpc.GetPreparationOutputRequest(pipelineUuid: pipelineUuid)) - .then((response) => _toOutputResponse(response.output))); - } - - @override - Future getGraphOutput( - String pipelineUuid, - RunCodeRequestWrapper request, - ) { - return _runSafely(() => _defaultClient - .getGraph(grpc.GetGraphRequest(pipelineUuid: pipelineUuid)) - .then((response) => OutputResponse(response.graph)) - .catchError((err) { - print(err); - return _toOutputResponse(''); - })); - } - - Future _runSafely(Future Function() invoke) async { - try { - return await invoke(); - } on GrpcError catch (error) { - throw RunCodeError(error.message); - } on Exception catch (_) { - throw RunCodeError(null); - } - } - - /// Run Code request should use different urls for each sdk - /// instead of the default one, because we need to code - /// sdk services for it - grpc.PlaygroundServiceClient _createRunCodeClient(SDK? sdk) { - String apiClientURL = kApiClientURL; - if (sdk != null) { - apiClientURL = sdk.getRoute; - } - IisWorkaroundChannel channel = IisWorkaroundChannel.xhr( - Uri.parse(apiClientURL), - ); - return grpc.PlaygroundServiceClient(channel); - } - - grpc.RunCodeRequest _toGrpcRequest(RunCodeRequestWrapper request) { - return grpc.RunCodeRequest() - ..code = request.code - ..sdk = _getGrpcSdk(request.sdk) - ..pipelineOptions = pipelineOptionsToString(request.pipelineOptions); - } - - grpc.Sdk _getGrpcSdk(SDK sdk) { - switch (sdk) { - case SDK.java: - return grpc.Sdk.SDK_JAVA; - case SDK.go: - return grpc.Sdk.SDK_GO; - case SDK.python: - return grpc.Sdk.SDK_PYTHON; - case SDK.scio: - return grpc.Sdk.SDK_SCIO; - } - } - - RunCodeStatus _toClientStatus(grpc.Status status) { - switch (status) { - case grpc.Status.STATUS_UNSPECIFIED: - return RunCodeStatus.unspecified; - case grpc.Status.STATUS_VALIDATING: - case grpc.Status.STATUS_PREPARING: - return RunCodeStatus.preparation; - case grpc.Status.STATUS_COMPILING: - return RunCodeStatus.compiling; - case grpc.Status.STATUS_EXECUTING: - return RunCodeStatus.executing; - case grpc.Status.STATUS_CANCELED: - case grpc.Status.STATUS_FINISHED: - return RunCodeStatus.finished; - case grpc.Status.STATUS_COMPILE_ERROR: - return RunCodeStatus.compileError; - case grpc.Status.STATUS_RUN_TIMEOUT: - return RunCodeStatus.timeout; - case grpc.Status.STATUS_RUN_ERROR: - return RunCodeStatus.runError; - case grpc.Status.STATUS_VALIDATION_ERROR: - return RunCodeStatus.validationError; - case grpc.Status.STATUS_PREPARATION_ERROR: - return RunCodeStatus.preparationError; - case grpc.Status.STATUS_ERROR: - return RunCodeStatus.unknownError; - } - return RunCodeStatus.unspecified; - } - - OutputResponse _toOutputResponse(String response) { - return OutputResponse(replaceIncorrectSymbols(response)); - } -} diff --git a/playground/frontend/lib/modules/examples/components/description_popover/description_popover.dart b/playground/frontend/lib/modules/examples/components/description_popover/description_popover.dart index a438aac5baed..e368973cb64b 100644 --- a/playground/frontend/lib/modules/examples/components/description_popover/description_popover.dart +++ b/playground/frontend/lib/modules/examples/components/description_popover/description_popover.dart @@ -22,13 +22,13 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/constants/assets.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:url_launcher/url_launcher.dart'; const kDescriptionWidth = 300.0; class DescriptionPopover extends StatelessWidget { - final ExampleModel example; + final ExampleBase example; const DescriptionPopover({Key? key, required this.example}) : super(key: key); diff --git a/playground/frontend/lib/modules/examples/components/description_popover/description_popover_button.dart b/playground/frontend/lib/modules/examples/components/description_popover/description_popover_button.dart index 5e935eb0328d..8df888ed76fa 100644 --- a/playground/frontend/lib/modules/examples/components/description_popover/description_popover_button.dart +++ b/playground/frontend/lib/modules/examples/components/description_popover/description_popover_button.dart @@ -19,14 +19,13 @@ import 'package:aligned_dialog/aligned_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/description_popover/description_popover.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; class DescriptionPopoverButton extends StatelessWidget { final BuildContext? parentContext; - final ExampleModel example; + final ExampleBase example; final Alignment followerAnchor; final Alignment targetAnchor; final void Function()? onOpen; @@ -52,7 +51,7 @@ class DescriptionPopoverButton extends StatelessWidget { splashRadius: kIconButtonSplashRadius, icon: Icon( Icons.info_outline_rounded, - color: ThemeColors.of(context).grey1Color, + color: Theme.of(context).extension()?.iconColor, ), tooltip: appLocale.exampleDescription, onPressed: () { @@ -69,7 +68,7 @@ class DescriptionPopoverButton extends StatelessWidget { void _showDescriptionPopover( BuildContext context, - ExampleModel example, + ExampleBase example, Alignment followerAnchor, Alignment targetAnchor, ) async { diff --git a/playground/frontend/lib/modules/examples/components/example_list/category_expansion_panel.dart b/playground/frontend/lib/modules/examples/components/example_list/category_expansion_panel.dart index c6dcf47271e6..049db9e5968e 100644 --- a/playground/frontend/lib/modules/examples/components/example_list/category_expansion_panel.dart +++ b/playground/frontend/lib/modules/examples/components/example_list/category_expansion_panel.dart @@ -22,12 +22,12 @@ import 'package:expansion_widget/expansion_widget.dart'; import 'package:flutter/material.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/example_list/expansion_panel_item.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; class CategoryExpansionPanel extends StatelessWidget { final String categoryName; final List examples; - final ExampleModel selectedExample; + final ExampleBase selectedExample; final AnimationController animationController; final OverlayEntry? dropdown; diff --git a/playground/frontend/lib/modules/examples/components/example_list/example_item_actions.dart b/playground/frontend/lib/modules/examples/components/example_list/example_item_actions.dart index 9cae77f6073b..584b4c157316 100644 --- a/playground/frontend/lib/modules/examples/components/example_list/example_item_actions.dart +++ b/playground/frontend/lib/modules/examples/components/example_list/example_item_actions.dart @@ -18,14 +18,13 @@ import 'package:flutter/material.dart'; import 'package:playground/modules/examples/components/description_popover/description_popover_button.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground/modules/examples/components/multifile_popover/multifile_popover_button.dart'; import 'package:playground/modules/examples/models/popover_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; -import '../multifile_popover/multifile_popover_button.dart'; - class ExampleItemActions extends StatelessWidget { - final ExampleModel example; + final ExampleBase example; final BuildContext parentContext; const ExampleItemActions( diff --git a/playground/frontend/lib/modules/examples/components/example_list/example_list.dart b/playground/frontend/lib/modules/examples/components/example_list/example_list.dart index 6b175e2692a1..1c9227d2f59e 100644 --- a/playground/frontend/lib/modules/examples/components/example_list/example_list.dart +++ b/playground/frontend/lib/modules/examples/components/example_list/example_list.dart @@ -18,15 +18,15 @@ import 'package:flutter/material.dart'; import 'package:playground/modules/examples/components/examples_components.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; import 'package:playground/pages/playground/states/example_selector_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class ExampleList extends StatelessWidget { final ScrollController controller; final AnimationController animationController; final OverlayEntry? dropdown; - final ExampleModel selectedExample; + final ExampleBase selectedExample; const ExampleList({ Key? key, @@ -50,7 +50,7 @@ class ExampleList extends StatelessWidget { itemCount: state.categories.length, itemBuilder: (context, index) => CategoryExpansionPanel( selectedExample: selectedExample, - categoryName: state.categories[index].name, + categoryName: state.categories[index].title, examples: state.categories[index].examples, animationController: animationController, dropdown: dropdown, diff --git a/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart b/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart index f212f3c07e4c..b035e35d2d99 100644 --- a/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart +++ b/playground/frontend/lib/modules/examples/components/example_list/expansion_panel_item.dart @@ -20,14 +20,12 @@ import 'package:flutter/material.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; import 'package:playground/modules/examples/components/example_list/example_item_actions.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class ExpansionPanelItem extends StatelessWidget { - final ExampleModel example; - final ExampleModel selectedExample; + final ExampleBase example; + final ExampleBase selectedExample; final AnimationController animationController; final OverlayEntry? dropdown; @@ -41,20 +39,21 @@ class ExpansionPanelItem extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, playgroundState, child) => MouseRegion( + return Consumer( + builder: (context, controller, child) => MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () async { - if (playgroundState.selectedExample != example) { - _closeDropdown(playgroundState.exampleState); + if (controller.selectedExample != example) { + _closeDropdown(controller.exampleCache); AnalyticsService.get(context).trackSelectExample(example); final exampleWithInfo = - await playgroundState.exampleState.loadExampleInfo(example); + await controller.exampleCache.loadExampleInfo(example); // TODO: setCurrentSdk = false when we do // per-SDK output and run status. // Now using true to reset the output and run status. - playgroundState.setExample(exampleWithInfo, setCurrentSdk: true); + // https://github.com/apache/beam/issues/23248 + controller.setExample(exampleWithInfo, setCurrentSdk: true); } }, child: Container( @@ -83,9 +82,9 @@ class ExpansionPanelItem extends StatelessWidget { ); } - void _closeDropdown(ExampleState exampleState) { + void _closeDropdown(ExampleCache exampleCache) { animationController.reverse(); dropdown?.remove(); - exampleState.changeSelectorVisibility(); + exampleCache.changeSelectorVisibility(); } } diff --git a/playground/frontend/lib/modules/examples/components/filter/category_bubble.dart b/playground/frontend/lib/modules/examples/components/filter/category_bubble.dart index aabe00735ee6..849f19db877c 100644 --- a/playground/frontend/lib/modules/examples/components/filter/category_bubble.dart +++ b/playground/frontend/lib/modules/examples/components/filter/category_bubble.dart @@ -17,10 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; import 'package:playground/pages/playground/states/example_selector_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class CategoryBubble extends StatelessWidget { @@ -35,46 +33,21 @@ class CategoryBubble extends StatelessWidget { @override Widget build(BuildContext context) { - return MouseRegion( - cursor: SystemMouseCursors.click, - child: Padding( - padding: const EdgeInsets.only(right: kMdSpacing), - child: Consumer( - builder: (context, state, child) { - final isSelected = type == state.selectedFilterType; + return Consumer( + builder: (context, state, child) { + final isSelected = type == state.selectedFilterType; - return GestureDetector( - onTap: () { - if (!isSelected) { - state.setSelectedFilterType(type); - state.sortCategories(); - } - }, - child: Container( - height: kContainerHeight, - padding: const EdgeInsets.symmetric(horizontal: kXlSpacing), - decoration: BoxDecoration( - color: isSelected - ? ThemeColors.of(context).primary - : ThemeColors.of(context).lightGreyColor, - borderRadius: BorderRadius.circular(kXlBorderRadius), - ), - child: Center( - child: Text( - name, - style: TextStyle( - color: isSelected - ? ThemeColors.of(context).primaryBackgroundTextColor - : ThemeColors.of(context) - .lightGreyBackgroundTextColor, - ), - ), - ), - ), - ); + return BubbleWidget( + isSelected: isSelected, + title: name, + onTap: () { + if (!isSelected) { + state.setSelectedFilterType(type); + state.sortCategories(); + } }, - ), - ), + ); + }, ); } } diff --git a/playground/frontend/lib/modules/examples/components/filter/type_filter.dart b/playground/frontend/lib/modules/examples/components/filter/type_filter.dart index c56f6fcde415..a221999f3ce6 100644 --- a/playground/frontend/lib/modules/examples/components/filter/type_filter.dart +++ b/playground/frontend/lib/modules/examples/components/filter/type_filter.dart @@ -20,7 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/examples_components.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; class TypeFilter extends StatelessWidget { const TypeFilter({Key? key}) : super(key: key); diff --git a/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover.dart b/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover.dart index 6d90bc7bfd56..c4fb93fa4706 100644 --- a/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover.dart +++ b/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover.dart @@ -22,13 +22,13 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/constants/assets.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:url_launcher/url_launcher.dart'; const kMultifileWidth = 300.0; class MultifilePopover extends StatelessWidget { - final ExampleModel example; + final ExampleBase example; const MultifilePopover({Key? key, required this.example}) : super(key: key); diff --git a/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover_button.dart b/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover_button.dart index 069830c65c76..d530aa645ebe 100644 --- a/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover_button.dart +++ b/playground/frontend/lib/modules/examples/components/multifile_popover/multifile_popover_button.dart @@ -23,11 +23,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/constants/assets.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/multifile_popover/multifile_popover.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'package:playground_components/playground_components.dart'; class MultifilePopoverButton extends StatelessWidget { final BuildContext? parentContext; - final ExampleModel example; + final ExampleBase example; final Alignment followerAnchor; final Alignment targetAnchor; final void Function()? onOpen; @@ -67,7 +67,7 @@ class MultifilePopoverButton extends StatelessWidget { void _showMultifilePopover( BuildContext context, - ExampleModel example, + ExampleBase example, Alignment followerAnchor, Alignment targetAnchor, ) async { diff --git a/playground/frontend/lib/modules/examples/components/search_field/search_field.dart b/playground/frontend/lib/modules/examples/components/search_field/search_field.dart index 80939ac15554..2095dfac1d23 100644 --- a/playground/frontend/lib/modules/examples/components/search_field/search_field.dart +++ b/playground/frontend/lib/modules/examples/components/search_field/search_field.dart @@ -18,9 +18,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/pages/playground/states/example_selector_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; const double kContainerWidth = 376.0; @@ -34,8 +34,11 @@ class SearchField extends StatelessWidget { @override Widget build(BuildContext context) { + final borderColor = + Theme.of(context).extension()!.borderColor; + final OutlineInputBorder border = OutlineInputBorder( - borderSide: BorderSide(color: ThemeColors.of(context).lightGreyColor), + borderSide: BorderSide(color: borderColor), borderRadius: BorderRadius.circular(kMdBorderRadius), ); @@ -61,7 +64,7 @@ class SearchField extends StatelessWidget { ), child: Icon( Icons.search, - color: ThemeColors.of(context).lightGreyColor, + color: borderColor, size: kIconSizeMd, ), ), @@ -72,7 +75,7 @@ class SearchField extends StatelessWidget { hintText: AppLocalizations.of(context)!.search, contentPadding: const EdgeInsets.only(left: kLgSpacing), ), - cursorColor: ThemeColors.of(context).lightGreyColor, + cursorColor: borderColor, cursorWidth: kCursorSize, textAlignVertical: TextAlignVertical.center, onFieldSubmitted: (String filterText) => diff --git a/playground/frontend/lib/modules/examples/example_selector.dart b/playground/frontend/lib/modules/examples/example_selector.dart index f65c45bfacfd..c08fdb3cea2d 100644 --- a/playground/frontend/lib/modules/examples/example_selector.dart +++ b/playground/frontend/lib/modules/examples/example_selector.dart @@ -18,18 +18,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/components/horizontal_divider/horizontal_divider.dart'; -import 'package:playground/components/loading_indicator/loading_indicator.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/links.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/examples/components/examples_components.dart'; import 'package:playground/modules/examples/components/outside_click_handler.dart'; import 'package:playground/modules/examples/models/popover_state.dart'; import 'package:playground/pages/playground/states/example_selector_state.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; import 'package:playground/utils/dropdown_utils.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -90,10 +86,10 @@ class _ExampleSelectorState extends State return Container( height: kContainerHeight, decoration: BoxDecoration( - color: ThemeColors.of(context).dropdownButton, + color: Theme.of(context).dividerColor, borderRadius: BorderRadius.circular(kSmBorderRadius), ), - child: Consumer( + child: Consumer( builder: (context, state, child) => TextButton( key: selectorKey, onPressed: () { @@ -111,7 +107,7 @@ class _ExampleSelectorState extends State alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [ - Consumer( + Consumer( builder: (context, state, child) => Text(state.examplesTitle), ), const Icon(Icons.keyboard_arrow_down), @@ -130,12 +126,12 @@ class _ExampleSelectorState extends State return ChangeNotifierProvider( create: (context) => PopoverState(false), builder: (context, state) { - return Consumer( - builder: (context, playgroundState, child) => Stack( + return Consumer( + builder: (context, playgroundController, child) => Stack( children: [ OutsideClickHandler( onTap: () { - _closeDropdown(playgroundState.exampleState); + _closeDropdown(playgroundController.exampleCache); // handle description dialogs Navigator.of(context, rootNavigator: true) .popUntil((route) { @@ -145,9 +141,9 @@ class _ExampleSelectorState extends State ), ChangeNotifierProvider( create: (context) => ExampleSelectorState( - playgroundState, - playgroundState.exampleState - .getCategories(playgroundState.sdk), + playgroundController, + playgroundController.exampleCache + .getCategories(playgroundController.sdk), ), builder: (context, _) => Positioned( left: dropdownOffset.dx, @@ -166,7 +162,7 @@ class _ExampleSelectorState extends State ), child: _buildDropdownContent( context, - playgroundState, + playgroundController, ), ), ), @@ -184,13 +180,11 @@ class _ExampleSelectorState extends State Widget _buildDropdownContent( BuildContext context, - PlaygroundState playgroundState, + PlaygroundController playgroundController, ) { - if (playgroundState.exampleState.sdkCategories == null || - playgroundState.selectedExample == null) { - return const LoadingIndicator( - size: kMdLoadingIndicatorSize, - ); + if (playgroundController.exampleCache.categoryListsBySdk.isEmpty || + playgroundController.selectedExample == null) { + return const LoadingIndicator(); } return Column( @@ -199,11 +193,11 @@ class _ExampleSelectorState extends State const TypeFilter(), ExampleList( controller: scrollController, - selectedExample: playgroundState.selectedExample!, + selectedExample: playgroundController.selectedExample!, animationController: animationController, dropdown: examplesDropdown, ), - const HorizontalDivider(indent: kLgSpacing), + const BeamDivider(), SizedBox( width: double.infinity, child: TextButton( @@ -213,7 +207,7 @@ class _ExampleSelectorState extends State alignment: Alignment.centerLeft, child: Text( AppLocalizations.of(context)!.addExample, - style: TextStyle(color: ThemeColors.of(context).primary), + style: TextStyle(color: Theme.of(context).primaryColor), ), ), ), @@ -224,9 +218,9 @@ class _ExampleSelectorState extends State ); } - void _closeDropdown(ExampleState exampleState) { + void _closeDropdown(ExampleCache exampleCache) { animationController.reverse(); examplesDropdown?.remove(); - exampleState.changeSelectorVisibility(); + exampleCache.changeSelectorVisibility(); } } diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart deleted file mode 100644 index 5841e0e294c6..000000000000 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_origin.dart'; - -class CatalogDefaultExampleLoadingDescriptor extends ExampleLoadingDescriptor { - final SDK sdk; - - const CatalogDefaultExampleLoadingDescriptor({ - required this.sdk, - }); - - @override - ExampleOrigin get origin => ExampleOrigin.catalogDefault; - - @override - int get hashCode => sdk.hashCode; - - @override - bool operator ==(Object other) { - return other is CatalogDefaultExampleLoadingDescriptor && sdk == other.sdk; - } - - // Only ContentExampleLoadingDescriptor is serialized now. - @override - Map toJson() => throw UnimplementedError(); -} diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart index ca9199f12855..7b3854d6f31f 100644 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart +++ b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart @@ -19,18 +19,11 @@ import 'dart:convert'; import 'package:playground/constants/params.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart'; import 'package:playground/modules/examples/models/example_token_type.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; class ExamplesLoadingDescriptorFactory { - static const _defaultSdk = SDK.java; + static const _defaultSdk = Sdk.java; static ExamplesLoadingDescriptor fromUriParts({ required String path, @@ -61,7 +54,7 @@ class ExamplesLoadingDescriptorFactory { return null; } - final sdk = SDK.tryParse(params[kSdkParam]); + final sdk = Sdk.tryParse(params[kSdkParam]); return ExamplesLoadingDescriptor( descriptors: _parseMultipleInstantExamples(list, sdk), @@ -75,7 +68,7 @@ class ExamplesLoadingDescriptorFactory { static List _parseMultipleInstantExamples( List list, - SDK? sdk, + Sdk? sdk, ) { final result = []; @@ -132,7 +125,7 @@ class ExamplesLoadingDescriptorFactory { return null; } - final sdk = SDK.tryParse(params[kSdkParam]) ?? _defaultSdk; + final sdk = Sdk.tryParse(params[kSdkParam]) ?? _defaultSdk; return ExamplesLoadingDescriptor( descriptors: [ @@ -161,14 +154,14 @@ class ExamplesLoadingDescriptorFactory { return ExamplesLoadingDescriptor( descriptors: [ EmptyExampleLoadingDescriptor( - sdk: SDK.tryParse(params[kSdkParam]) ?? _defaultSdk, + sdk: Sdk.tryParse(params[kSdkParam]) ?? _defaultSdk, ), ], lazyLoadDescriptors: _emptyLazyLoadDescriptors, ); } - static Map> _getLazyLoadDescriptors() { + static Map> _getLazyLoadDescriptors() { if (isEmbedded()) { return _emptyLazyLoadDescriptors; } @@ -176,18 +169,18 @@ class ExamplesLoadingDescriptorFactory { return _defaultLazyLoadDescriptors; } - static Map> + static Map> get _emptyLazyLoadDescriptors { return { - for (final sdk in SDK.values) + for (final sdk in Sdk.known) sdk: [EmptyExampleLoadingDescriptor(sdk: sdk)] }; } - static Map> + static Map> get _defaultLazyLoadDescriptors { return { - for (final sdk in SDK.values) + for (final sdk in Sdk.known) sdk: [CatalogDefaultExampleLoadingDescriptor(sdk: sdk)] }; } diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart deleted file mode 100644 index a5adbff51ac6..000000000000 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_origin.dart'; - -class StandardExampleLoadingDescriptor extends ExampleLoadingDescriptor { - final String path; - - const StandardExampleLoadingDescriptor({ - required this.path, - }); - - @override - ExampleOrigin get origin => ExampleOrigin.standard; - - @override - String toString() => '$origin-$path'; - - @override - int get hashCode => path.hashCode; - - @override - bool operator ==(Object other) { - return other is StandardExampleLoadingDescriptor && path == other.path; - } - - // Only ContentExampleLoadingDescriptor is serialized now. - @override - Map toJson() => throw UnimplementedError(); -} diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart b/playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart deleted file mode 100644 index 26e58a8453fb..000000000000 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_origin.dart'; - -class UserSharedExampleLoadingDescriptor extends ExampleLoadingDescriptor { - final String snippetId; - - const UserSharedExampleLoadingDescriptor({ - required this.snippetId, - }); - - @override - ExampleOrigin get origin => ExampleOrigin.userShared; - - @override - String toString() => '$origin-$snippetId'; - - @override - int get hashCode => snippetId.hashCode; - - @override - bool operator ==(Object other) { - return other is UserSharedExampleLoadingDescriptor && - snippetId == other.snippetId; - } - - // Only ContentExampleLoadingDescriptor is serialized now. - @override - Map toJson() => throw UnimplementedError(); -} diff --git a/playground/frontend/lib/modules/examples/models/example_token_type.dart b/playground/frontend/lib/modules/examples/models/example_token_type.dart index 789b1c3a179a..48c7c5806358 100644 --- a/playground/frontend/lib/modules/examples/models/example_token_type.dart +++ b/playground/frontend/lib/modules/examples/models/example_token_type.dart @@ -16,7 +16,7 @@ * limitations under the License. */ -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; enum ExampleTokenType { standard, @@ -24,7 +24,7 @@ enum ExampleTokenType { ; static ExampleTokenType fromToken(String token) { - final sdk = SDK.tryParseExamplePath(token); + final sdk = Sdk.tryParseExamplePath(token); if (sdk != null) { return standard; } diff --git a/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart b/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart deleted file mode 100644 index 039a567c5efa..000000000000 --- a/playground/frontend/lib/modules/examples/repositories/example_client/example_client.dart +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart'; - -abstract class ExampleClient { - Future getListOfExamples( - GetListOfExamplesRequestWrapper request, - ); - - Future getExampleSource( - GetExampleRequestWrapper request, - ); - - Future getDefaultExample( - GetExampleRequestWrapper request, - ); - - Future getExample( - GetExampleRequestWrapper request, - ); - - Future getExampleOutput( - GetExampleRequestWrapper request, - ); - - Future getExampleLogs( - GetExampleRequestWrapper request, - ); - - Future getExampleGraph( - GetExampleRequestWrapper request, - ); - - Future getSnippet( - GetSnippetRequestWrapper request, - ); - - Future saveSnippet( - SaveSnippetRequestWrapper request, - ); -} diff --git a/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart b/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart deleted file mode 100644 index f4a11be58aae..000000000000 --- a/playground/frontend/lib/modules/examples/repositories/example_client/grpc_example_client.dart +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:grpc/grpc_web.dart'; -import 'package:playground/api/iis_workaround_channel.dart'; -import 'package:playground/api/v1/api.pbgrpc.dart' as grpc; -import 'package:playground/config.g.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/examples/repositories/example_client/example_client.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart'; -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/utils/replace_incorrect_symbols.dart'; - -class GrpcExampleClient implements ExampleClient { - late final grpc.PlaygroundServiceClient _defaultClient; - - GrpcExampleClient() { - final channel = IisWorkaroundChannel.xhr( - Uri.parse(kApiClientURL), - ); - _defaultClient = grpc.PlaygroundServiceClient(channel); - } - - @override - Future getListOfExamples( - GetListOfExamplesRequestWrapper request, - ) { - return _runSafely( - () => _defaultClient - .getPrecompiledObjects( - _getListOfExamplesRequestToGrpcRequest(request)) - .then((response) => GetListOfExampleResponse( - _toClientCategories(response.sdkCategories))), - ); - } - - @override - Future getDefaultExample( - GetExampleRequestWrapper request, - ) { - return _runSafely( - () => _defaultClient - .getDefaultPrecompiledObject( - _getDefaultExampleRequestToGrpcRequest(request)) - .then( - (response) => GetExampleResponse( - _toExampleModel( - request.sdk, - response.precompiledObject, - ), - ), - ), - ); - } - - @override - Future getExample( - GetExampleRequestWrapper request, - ) { - return _runSafely( - () => _defaultClient - .getPrecompiledObject( - grpc.GetPrecompiledObjectRequest()..cloudPath = request.path) - .then( - (response) => GetExampleResponse( - _toExampleModel( - request.sdk, - response.precompiledObject, - ), - ), - ), - ); - } - - @override - Future getExampleSource( - GetExampleRequestWrapper request) { - return _runSafely( - () => _defaultClient - .getPrecompiledObjectCode( - _getExampleCodeRequestToGrpcRequest(request)) - .then((response) => - GetExampleCodeResponse(replaceIncorrectSymbols(response.code))), - ); - } - - @override - Future getExampleOutput(GetExampleRequestWrapper request) { - return _runSafely( - () => _defaultClient - .getPrecompiledObjectOutput( - _getExampleOutputRequestToGrpcRequest(request)) - .then((response) => - OutputResponse(replaceIncorrectSymbols(response.output))) - .catchError((err) { - print(err); - return OutputResponse(''); - }), - ); - } - - @override - Future getExampleLogs(GetExampleRequestWrapper request) { - return _runSafely( - () => _defaultClient - .getPrecompiledObjectLogs(_getExampleLogRequestToGrpcRequest(request)) - .then((response) => - OutputResponse(replaceIncorrectSymbols(response.output))) - .catchError((err) { - print(err); - return OutputResponse(''); - }), - ); - } - - @override - Future getExampleGraph(GetExampleRequestWrapper request) { - return _runSafely( - () => _defaultClient - .getPrecompiledObjectGraph( - _getExampleGraphRequestToGrpcRequest(request)) - .then((response) => OutputResponse(response.graph)) - .catchError((err) { - print(err); - return OutputResponse(''); - }), - ); - } - - @override - Future getSnippet( - GetSnippetRequestWrapper request, - ) { - return _runSafely( - () => _defaultClient - .getSnippet(_getSnippetRequestToGrpcRequest(request)) - .then( - (response) => GetSnippetResponse( - files: _convertToSharedFileList(response.files), - sdk: _getAppSdk(response.sdk), - pipelineOptions: response.pipelineOptions, - ), - ), - ); - } - - @override - Future saveSnippet( - SaveSnippetRequestWrapper request, - ) { - return _runSafely( - () => _defaultClient - .saveSnippet(_saveSnippetRequestToGrpcRequest(request)) - .then( - (response) => SaveSnippetResponse( - id: response.id, - ), - ), - ); - } - - Future _runSafely(Future Function() invoke) { - try { - return invoke(); - } on GrpcError catch (error) { - throw Exception(error.message); - } - } - - grpc.GetPrecompiledObjectsRequest _getListOfExamplesRequestToGrpcRequest( - GetListOfExamplesRequestWrapper request, - ) { - return grpc.GetPrecompiledObjectsRequest() - ..category = request.category ?? '' - ..sdk = request.sdk == null - ? grpc.Sdk.SDK_UNSPECIFIED - : _getGrpcSdk(request.sdk!); - } - - grpc.GetDefaultPrecompiledObjectRequest - _getDefaultExampleRequestToGrpcRequest( - GetExampleRequestWrapper request, - ) { - return grpc.GetDefaultPrecompiledObjectRequest() - ..sdk = _getGrpcSdk(request.sdk); - } - - grpc.GetPrecompiledObjectCodeRequest _getExampleCodeRequestToGrpcRequest( - GetExampleRequestWrapper request, - ) { - return grpc.GetPrecompiledObjectCodeRequest()..cloudPath = request.path; - } - - grpc.GetPrecompiledObjectOutputRequest _getExampleOutputRequestToGrpcRequest( - GetExampleRequestWrapper request, - ) { - return grpc.GetPrecompiledObjectOutputRequest()..cloudPath = request.path; - } - - grpc.GetPrecompiledObjectLogsRequest _getExampleLogRequestToGrpcRequest( - GetExampleRequestWrapper request, - ) { - return grpc.GetPrecompiledObjectLogsRequest()..cloudPath = request.path; - } - - grpc.GetPrecompiledObjectGraphRequest _getExampleGraphRequestToGrpcRequest( - GetExampleRequestWrapper request, - ) { - return grpc.GetPrecompiledObjectGraphRequest()..cloudPath = request.path; - } - - grpc.GetSnippetRequest _getSnippetRequestToGrpcRequest( - GetSnippetRequestWrapper request, - ) { - return grpc.GetSnippetRequest()..id = request.id; - } - - grpc.SaveSnippetRequest _saveSnippetRequestToGrpcRequest( - SaveSnippetRequestWrapper request, - ) { - return grpc.SaveSnippetRequest() - ..sdk = _getGrpcSdk(request.sdk) - ..pipelineOptions = request.pipelineOptions - ..files.addAll(_convertToSnippetFileList(request.files)); - } - - grpc.Sdk _getGrpcSdk(SDK sdk) { - switch (sdk) { - case SDK.java: - return grpc.Sdk.SDK_JAVA; - case SDK.go: - return grpc.Sdk.SDK_GO; - case SDK.python: - return grpc.Sdk.SDK_PYTHON; - case SDK.scio: - return grpc.Sdk.SDK_SCIO; - } - } - - SDK _getAppSdk(grpc.Sdk sdk) { - switch (sdk) { - case grpc.Sdk.SDK_JAVA: - return SDK.java; - case grpc.Sdk.SDK_GO: - return SDK.go; - case grpc.Sdk.SDK_PYTHON: - return SDK.python; - case grpc.Sdk.SDK_SCIO: - return SDK.scio; - default: - return SDK.java; - } - } - - ExampleType _exampleTypeFromString(grpc.PrecompiledObjectType type) { - switch (type) { - case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_EXAMPLE: - return ExampleType.example; - case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_KATA: - return ExampleType.kata; - case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_UNIT_TEST: - return ExampleType.test; - case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_UNSPECIFIED: - return ExampleType.all; - default: - return ExampleType.example; - } - } - - Map> _toClientCategories( - List response, - ) { - Map> sdkCategoriesMap = {}; - List>> entries = []; - for (var sdkMap in response) { - SDK sdk = _getAppSdk(sdkMap.sdk); - List categoriesForSdk = []; - for (var category in sdkMap.categories) { - List examples = category.precompiledObjects - .map((example) => _toExampleModel(sdk, example)) - .toList() - ..sort(); - categoriesForSdk.add(CategoryModel( - name: category.categoryName, - examples: examples, - )); - } - entries.add(MapEntry(sdk, categoriesForSdk..sort())); - } - sdkCategoriesMap.addEntries(entries); - return sdkCategoriesMap; - } - - ExampleModel _toExampleModel(SDK sdk, grpc.PrecompiledObject example) { - return ExampleModel( - sdk: sdk, - name: example.name, - description: example.description, - type: _exampleTypeFromString(example.type), - path: example.cloudPath, - contextLine: example.contextLine, - pipelineOptions: example.pipelineOptions, - isMultiFile: example.multifile, - link: example.link, - ); - } - - List _convertToSharedFileList( - List snippetFileList, - ) { - final sharedFilesList = []; - - for (grpc.SnippetFile item in snippetFileList) { - sharedFilesList.add(SharedFile( - code: item.content, - isMain: item.isMain, - name: item.name, - )); - } - - return sharedFilesList; - } - - List _convertToSnippetFileList( - List sharedFilesList, - ) { - final snippetFileList = []; - - for (SharedFile item in sharedFilesList) { - snippetFileList.add( - grpc.SnippetFile() - ..name = item.name - ..isMain = true - ..content = item.code, - ); - } - - return snippetFileList; - } -} diff --git a/playground/frontend/lib/modules/examples/repositories/example_repository.dart b/playground/frontend/lib/modules/examples/repositories/example_repository.dart deleted file mode 100644 index 65c09f885fba..000000000000 --- a/playground/frontend/lib/modules/examples/repositories/example_repository.dart +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/examples/repositories/example_client/example_client.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -class ExampleRepository { - late final ExampleClient _client; - - ExampleRepository(ExampleClient client) { - _client = client; - } - - Future>> getListOfExamples( - GetListOfExamplesRequestWrapper request, - ) async { - final result = await _client.getListOfExamples(request); - return result.categories; - } - - Future getDefaultExample( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getDefaultExample(request); - return result.example; - } - - Future getExampleSource( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getExampleSource(request); - return result.code; - } - - Future getExampleOutput( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getExampleOutput(request); - return result.output; - } - - Future getExampleLogs( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getExampleLogs(request); - return result.output; - } - - Future getExampleGraph( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getExampleGraph(request); - return result.output; - } - - Future getExample( - GetExampleRequestWrapper request, - ) async { - final result = await _client.getExample(request); - return result.example; - } - - Future getSnippet( - GetSnippetRequestWrapper request, - ) async { - final result = await _client.getSnippet(request); - return result; - } - - Future saveSnippet( - SaveSnippetRequestWrapper request, - ) async { - final result = await _client.saveSnippet(request); - return result.id; - } -} diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_example_request.dart b/playground/frontend/lib/modules/examples/repositories/models/get_example_request.dart deleted file mode 100644 index da87f09b2881..000000000000 --- a/playground/frontend/lib/modules/examples/repositories/models/get_example_request.dart +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/cupertino.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -class GetExampleRequestWrapper { - final String path; - final SDK sdk; - - GetExampleRequestWrapper(this.path, this.sdk); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is GetExampleRequestWrapper && - path == other.path && - sdk == other.sdk; - - @override - int get hashCode => hashValues(path.hashCode, sdk.hashCode); -} diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_request.dart b/playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_request.dart deleted file mode 100644 index 0d39df522d5c..000000000000 --- a/playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_request.dart +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/cupertino.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -class GetListOfExamplesRequestWrapper { - final SDK? sdk; - final String? category; - - GetListOfExamplesRequestWrapper({required this.sdk, required this.category}); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is GetListOfExamplesRequestWrapper && - category == other.category && - sdk == other.sdk; - - @override - int get hashCode => hashValues(category.hashCode, sdk.hashCode); -} diff --git a/playground/frontend/lib/modules/messages/handlers/messages_handler.dart b/playground/frontend/lib/modules/messages/handlers/messages_handler.dart index 3a4047afbb52..12538d7daecb 100644 --- a/playground/frontend/lib/modules/messages/handlers/messages_handler.dart +++ b/playground/frontend/lib/modules/messages/handlers/messages_handler.dart @@ -20,16 +20,16 @@ import 'package:playground/modules/messages/handlers/abstract_message_handler.da import 'package:playground/modules/messages/handlers/set_content_message_handler.dart'; import 'package:playground/modules/messages/handlers/set_sdk_message_handler.dart'; import 'package:playground/modules/messages/models/abstract_message.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; class MessagesHandler extends AbstractMessageHandler { final List handlers; MessagesHandler({ - required PlaygroundState playgroundState, + required PlaygroundController playgroundController, }) : handlers = [ - SetContentMessageHandler(playgroundState: playgroundState), - SetSdkMessageHandler(playgroundState: playgroundState), + SetContentMessageHandler(playgroundController: playgroundController), + SetSdkMessageHandler(playgroundController: playgroundController), ]; @override diff --git a/playground/frontend/lib/modules/messages/handlers/set_content_message_handler.dart b/playground/frontend/lib/modules/messages/handlers/set_content_message_handler.dart index 1e16e397f6e0..c2424b38af64 100644 --- a/playground/frontend/lib/modules/messages/handlers/set_content_message_handler.dart +++ b/playground/frontend/lib/modules/messages/handlers/set_content_message_handler.dart @@ -19,13 +19,13 @@ import 'package:playground/modules/messages/handlers/abstract_message_handler.dart'; import 'package:playground/modules/messages/models/abstract_message.dart'; import 'package:playground/modules/messages/models/set_content_message.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; class SetContentMessageHandler extends AbstractMessageHandler { - final PlaygroundState playgroundState; + final PlaygroundController playgroundController; const SetContentMessageHandler({ - required this.playgroundState, + required this.playgroundController, }); @override @@ -39,6 +39,6 @@ class SetContentMessageHandler extends AbstractMessageHandler { } void _handle(SetContentMessage message) { - playgroundState.examplesLoader.load(message.descriptor); + playgroundController.examplesLoader.load(message.descriptor); } } diff --git a/playground/frontend/lib/modules/messages/handlers/set_sdk_message_handler.dart b/playground/frontend/lib/modules/messages/handlers/set_sdk_message_handler.dart index 9a2595f9215c..2d066a9e8462 100644 --- a/playground/frontend/lib/modules/messages/handlers/set_sdk_message_handler.dart +++ b/playground/frontend/lib/modules/messages/handlers/set_sdk_message_handler.dart @@ -19,13 +19,13 @@ import 'package:playground/modules/messages/handlers/abstract_message_handler.dart'; import 'package:playground/modules/messages/models/abstract_message.dart'; import 'package:playground/modules/messages/models/set_sdk_message.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; class SetSdkMessageHandler extends AbstractMessageHandler { - final PlaygroundState playgroundState; + final PlaygroundController playgroundController; const SetSdkMessageHandler({ - required this.playgroundState, + required this.playgroundController, }); @override @@ -39,6 +39,6 @@ class SetSdkMessageHandler extends AbstractMessageHandler { } void _handle(SetSdkMessage message) { - playgroundState.setSdk(message.sdk); + playgroundController.setSdk(message.sdk); } } diff --git a/playground/frontend/lib/modules/messages/models/set_content_message.dart b/playground/frontend/lib/modules/messages/models/set_content_message.dart index 707bf5deb68b..fd8e1a04ba8f 100644 --- a/playground/frontend/lib/modules/messages/models/set_content_message.dart +++ b/playground/frontend/lib/modules/messages/models/set_content_message.dart @@ -16,9 +16,9 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart'; import 'package:playground/modules/messages/models/abstract_message.dart'; +import 'package:playground_components/playground_components.dart'; /// A message that sets content for multiple snippets. /// diff --git a/playground/frontend/lib/modules/messages/models/set_sdk_message.dart b/playground/frontend/lib/modules/messages/models/set_sdk_message.dart index ed195fd8013c..af3841afadfb 100644 --- a/playground/frontend/lib/modules/messages/models/set_sdk_message.dart +++ b/playground/frontend/lib/modules/messages/models/set_sdk_message.dart @@ -17,13 +17,13 @@ */ import 'package:playground/modules/messages/models/abstract_message.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; /// A message that switches the SDK. /// /// Sent to iframes by Beam documentation HTML when the language is switched. class SetSdkMessage extends AbstractMessage { - final SDK sdk; + final Sdk sdk; static const type = 'SetSdk'; @@ -36,7 +36,7 @@ class SetSdkMessage extends AbstractMessage { return null; } - final sdk = SDK.tryParse(map['sdk']); + final sdk = Sdk.tryParse(map['sdk']); if (sdk == null) { return null; } @@ -63,7 +63,7 @@ class SetSdkMessage extends AbstractMessage { @override Map toJson() { return { - 'sdk': sdk.name, + 'sdk': sdk.id, }; } } diff --git a/playground/frontend/lib/modules/output/components/output_area.dart b/playground/frontend/lib/modules/output/components/output_area.dart deleted file mode 100644 index 1492124ef356..000000000000 --- a/playground/frontend/lib/modules/output/components/output_area.dart +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:playground/modules/graph/graph_builder/painters/graph_painter.dart'; -import 'package:playground/modules/output/components/graph.dart'; -import 'package:playground/modules/output/components/output_result.dart'; -import 'package:playground/modules/output/models/output_placement.dart'; -import 'package:playground/modules/output/models/output_placement_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:provider/provider.dart'; - -class OutputArea extends StatelessWidget { - final TabController tabController; - final bool showGraph; - - const OutputArea({ - Key? key, - required this.tabController, - required this.showGraph, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - color: Theme.of(context).backgroundColor, - child: Consumer2( - builder: (context, playgroundState, placementState, child) { - final sdk = playgroundState.sdk; - - return TabBarView( - controller: tabController, - physics: const NeverScrollableScrollPhysics(), - children: [ - OutputResult( - text: playgroundState.outputResult, - isSelected: tabController.index == 0, - ), - if (showGraph) - sdk == null - ? Container() - : GraphTab( - graph: playgroundState.result?.graph ?? '', - sdk: sdk, - direction: _getGraphDirection(placementState.placement), - ), - ], - ); - }, - ), - ); - } - - GraphDirection _getGraphDirection(OutputPlacement placement) { - return placement == OutputPlacement.bottom - ? GraphDirection.horizontal - : GraphDirection.vertical; - } -} diff --git a/playground/frontend/lib/modules/output/components/output_header/result_filter_bubble.dart b/playground/frontend/lib/modules/output/components/output_header/result_filter_bubble.dart deleted file mode 100644 index d4401e775613..000000000000 --- a/playground/frontend/lib/modules/output/components/output_header/result_filter_bubble.dart +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/outputs_model.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:provider/provider.dart'; - -class ResultFilterBubble extends StatelessWidget { - final OutputType type; - final String name; - - const ResultFilterBubble({ - Key? key, - required this.type, - required this.name, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return MouseRegion( - cursor: SystemMouseCursors.click, - child: Padding( - padding: const EdgeInsets.only(right: kMdSpacing), - child: Consumer( - builder: (context, state, child) { - final isSelected = type == state.selectedOutputFilterType; - - return GestureDetector( - onTap: () { - if (!isSelected) { - state.setSelectedOutputFilterType(type); - state.filterOutput(type); - } - }, - child: Container( - height: kContainerHeight, - padding: const EdgeInsets.symmetric(horizontal: kXlSpacing), - decoration: BoxDecoration( - color: isSelected - ? ThemeColors.of(context).primary - : ThemeColors.of(context).lightGreyColor, - borderRadius: BorderRadius.circular(kXlBorderRadius), - ), - child: Center( - child: Text( - name, - style: TextStyle( - color: isSelected - ? ThemeColors.of(context).primaryBackgroundTextColor - : ThemeColors.of(context) - .lightGreyBackgroundTextColor, - ), - ), - ), - ), - ); - }, - ), - ), - ); - } -} diff --git a/playground/frontend/lib/modules/output/components/output_header/result_filter_popover.dart b/playground/frontend/lib/modules/output/components/output_header/result_filter_popover.dart deleted file mode 100644 index d93d79e5b1fc..000000000000 --- a/playground/frontend/lib/modules/output/components/output_header/result_filter_popover.dart +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/outputs_model.dart'; -import 'package:playground/modules/output/components/output_header/result_filter_bubble.dart'; - -const kPopoverWidth = 240.0; -const kPopoverPadding = 50.0; - -class ResultFilterPopover extends StatelessWidget { - const ResultFilterPopover({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - AppLocalizations appLocale = AppLocalizations.of(context)!; - - return Padding( - padding: const EdgeInsets.only(top: kPopoverPadding), - child: SizedBox( - width: kPopoverWidth, - child: Card( - child: Padding( - padding: const EdgeInsets.all(kMdSpacing), - child: Wrap( - runSpacing: kMdSpacing, - children: [ - Text(appLocale.displayAtThisTab), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: kSmSpacing, - vertical: kSmSpacing, - ), - child: Row( - children: [ - ResultFilterBubble( - type: OutputType.all, - name: appLocale.all, - ), - ResultFilterBubble( - type: OutputType.log, - name: appLocale.log, - ), - ResultFilterBubble( - type: OutputType.output, - name: appLocale.output, - ), - ], - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/playground/frontend/lib/modules/output/models/output_placement.dart b/playground/frontend/lib/modules/output/models/output_placement.dart index d15c9d36c384..642553751ccd 100644 --- a/playground/frontend/lib/modules/output/models/output_placement.dart +++ b/playground/frontend/lib/modules/output/models/output_placement.dart @@ -24,6 +24,13 @@ enum OutputPlacement { right, left, bottom, + ; + + Axis get graphDirection { + return this == OutputPlacement.bottom + ? Axis.horizontal + : Axis.vertical; + } } extension OutputPlacementToIcon on OutputPlacement { diff --git a/playground/frontend/lib/modules/sdk/components/sdk_selector.dart b/playground/frontend/lib/modules/sdk/components/sdk_selector.dart index c8197e8acbc3..c4c1656a1748 100644 --- a/playground/frontend/lib/modules/sdk/components/sdk_selector.dart +++ b/playground/frontend/lib/modules/sdk/components/sdk_selector.dart @@ -21,8 +21,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/components/dropdown_button/dropdown_button.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/sdk/components/sdk_selector_row.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; const kEmptyExampleName = 'Catalog'; @@ -31,8 +30,8 @@ const double kWidth = 150; const double kHeight = 172; class SDKSelector extends StatelessWidget { - final SDK? value; - final ValueChanged onChanged; + final Sdk? value; + final ValueChanged onChanged; const SDKSelector({ Key? key, @@ -45,35 +44,35 @@ class SDKSelector extends StatelessWidget { final localizations = AppLocalizations.of(context)!; final text = value == null ? localizations.selectSdkPlaceholder - : 'SDK: ${value?.displayName}'; + : 'SDK: ${value?.title}'; return Semantics( container: true, button: true, label: localizations.selectSdkDropdownSemantics, - child: AppDropdownButton( - buttonText: Text(text), - createDropdown: (close) => Column( - children: [ - const SizedBox(height: kMdSpacing), - ...SDK.values.map((SDK value) { - return SizedBox( - width: double.infinity, - child: Consumer( - builder: (context, state, child) => SdkSelectorRow( + child: Consumer( + builder: (context, controller, child) => AppDropdownButton( + buttonText: Text(text), + createDropdown: (close) => Column( + children: [ + const SizedBox(height: kMdSpacing), + ...Sdk.known.map((Sdk value) { + return SizedBox( + width: double.infinity, + child: SdkSelectorRow( sdk: value, onSelect: () { close(); onChanged(value); }, ), - ), - ); - }), - ], + ); + }), + ], + ), + width: kWidth, + height: kHeight, ), - width: kWidth, - height: kHeight, ), ); } diff --git a/playground/frontend/lib/modules/sdk/components/sdk_selector_row.dart b/playground/frontend/lib/modules/sdk/components/sdk_selector_row.dart index d66dda2dee7f..7993723bf25d 100644 --- a/playground/frontend/lib/modules/sdk/components/sdk_selector_row.dart +++ b/playground/frontend/lib/modules/sdk/components/sdk_selector_row.dart @@ -19,10 +19,10 @@ import 'package:flutter/material.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; class SdkSelectorRow extends StatelessWidget { - final SDK sdk; + final Sdk sdk; final VoidCallback onSelect; const SdkSelectorRow({ @@ -48,7 +48,7 @@ class SdkSelectorRow extends StatelessWidget { onPressed: onSelect, child: Padding( padding: const EdgeInsets.all(kLgSpacing), - child: Text(sdk.displayName), + child: Text(sdk.title), ), ); } diff --git a/playground/frontend/lib/modules/sdk/models/sdk.dart b/playground/frontend/lib/modules/sdk/models/sdk.dart deleted file mode 100644 index ef1fd6f57283..000000000000 --- a/playground/frontend/lib/modules/sdk/models/sdk.dart +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:highlight/highlight.dart'; -import 'package:highlight/languages/go.dart'; -import 'package:highlight/languages/java.dart'; -import 'package:highlight/languages/python.dart'; -import 'package:highlight/languages/scala.dart'; -import 'package:playground/config.g.dart'; - -enum SDK { - java, - go, - python, - scio, - ; - - /// A temporary solution while we wait for the backend to add - /// sdk in example responses. - static SDK? tryParseExamplePath(String? path) { - if (path == null) { - return null; - } - - if (path.startsWith('SDK_JAVA')) { - return java; - } - - if (path.startsWith('SDK_GO')) { - return go; - } - - if (path.startsWith('SDK_PYTHON')) { - return python; - } - - if (path.startsWith('SDK_SCIO')) { - return scio; - } - - return null; - } - - static SDK? tryParse(Object? value) { - if (value is! String) { - return null; - } - - try { - return values.byName(value); - } catch (ex) { - return null; - } - } -} - -extension SDKToString on SDK { - String get displayName { - switch (this) { - case SDK.go: - return 'Go'; - case SDK.java: - return 'Java'; - case SDK.python: - return 'Python'; - case SDK.scio: - return 'SCIO'; - } - } -} - -extension SdkToRoute on SDK { - String get getRoute { - switch (this) { - case SDK.java: - return kApiJavaClientURL; - case SDK.go: - return kApiGoClientURL; - case SDK.python: - return kApiPythonClientURL; - case SDK.scio: - return kApiScioClientURL; - default: - return ''; - } - } -} - -extension SdkToHighlightMode on SDK { - Mode get highlightMode { - switch (this) { - case SDK.java: - return java; - case SDK.go: - return go; - case SDK.python: - return python; - case SDK.scio: - return scala; - } - } -} diff --git a/playground/frontend/lib/modules/shortcuts/components/shortcut_row.dart b/playground/frontend/lib/modules/shortcuts/components/shortcut_row.dart index 538c20cdecfa..27cd34357038 100644 --- a/playground/frontend/lib/modules/shortcuts/components/shortcut_row.dart +++ b/playground/frontend/lib/modules/shortcuts/components/shortcut_row.dart @@ -18,11 +18,10 @@ import 'package:flutter/material.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/shortcuts/models/shortcut.dart'; -import 'package:playground/modules/shortcuts/utils/shortcuts_display_name.dart'; +import 'package:playground_components/playground_components.dart'; class ShortcutRow extends StatelessWidget { - final Shortcut shortcut; + final BeamShortcut shortcut; const ShortcutRow({Key? key, required this.shortcut}) : super(key: key); @@ -39,7 +38,7 @@ class ShortcutRow extends StatelessWidget { ), padding: const EdgeInsets.all(kMdSpacing), child: Text( - getShortcutDisplayName(shortcut), + shortcut.title, style: TextStyle(color: primaryColor), ), ), diff --git a/playground/frontend/lib/modules/shortcuts/components/shortcuts_manager.dart b/playground/frontend/lib/modules/shortcuts/components/shortcuts_manager.dart index 32d3ce26151f..5a9f1bc18453 100644 --- a/playground/frontend/lib/modules/shortcuts/components/shortcuts_manager.dart +++ b/playground/frontend/lib/modules/shortcuts/components/shortcuts_manager.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/modules/shortcuts/models/shortcut.dart'; +import 'package:playground_components/playground_components.dart'; class ShortcutsManager extends StatelessWidget { final Widget child; - final List shortcuts; + final List shortcuts; const ShortcutsManager({ Key? key, diff --git a/playground/frontend/lib/modules/shortcuts/components/shortcuts_modal.dart b/playground/frontend/lib/modules/shortcuts/components/shortcuts_modal.dart index c92c2748756f..2c5ee6a775a0 100644 --- a/playground/frontend/lib/modules/shortcuts/components/shortcuts_modal.dart +++ b/playground/frontend/lib/modules/shortcuts/components/shortcuts_modal.dart @@ -16,12 +16,14 @@ * limitations under the License. */ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/shortcuts/components/shortcut_row.dart'; import 'package:playground/modules/shortcuts/constants/global_shortcuts.dart'; +import 'package:playground_components/playground_components.dart'; const kButtonBorderRadius = 24.0; const kButtonWidth = 120.0; @@ -29,7 +31,11 @@ const kButtonHeight = 40.0; const kDialogPadding = 40.0; class ShortcutsModal extends StatelessWidget { - const ShortcutsModal({Key? key}) : super(key: key); + final PlaygroundController playgroundController; + + const ShortcutsModal({ + required this.playgroundController, + }); @override Widget build(BuildContext context) { @@ -50,7 +56,10 @@ class ShortcutsModal extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.start, runSpacing: kXlSpacing, children: [ - ...globalShortcuts.map( + ...[ + ...playgroundController.shortcuts, + ...globalShortcuts, + ].map( (shortcut) => Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -58,7 +67,8 @@ class ShortcutsModal extends StatelessWidget { Expanded( flex: 3, child: Text( - localize(context, shortcut.name), + shortcut.actionIntent.slug.tr(), + //localize(context, shortcut.name), style: const TextStyle(fontWeight: kBoldWeight), ), ), @@ -84,21 +94,4 @@ class ShortcutsModal extends StatelessWidget { ], ); } - - String localize(BuildContext context, String shortcutName) { - AppLocalizations appLocale = AppLocalizations.of(context)!; - - switch(shortcutName) { - case 'Run': - return appLocale.run; - case 'Reset': - return appLocale.reset; - case 'Clear Output': - return appLocale.clearOutput; - case 'New Example': - return appLocale.newExample; - default: - return shortcutName; - } - } } diff --git a/playground/frontend/lib/modules/shortcuts/constants/global_shortcuts.dart b/playground/frontend/lib/modules/shortcuts/constants/global_shortcuts.dart index bf3b14e7a73b..03797eaff3a0 100644 --- a/playground/frontend/lib/modules/shortcuts/constants/global_shortcuts.dart +++ b/playground/frontend/lib/modules/shortcuts/constants/global_shortcuts.dart @@ -18,93 +18,44 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:playground/modules/shortcuts/models/shortcut.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; -const kRunText = 'Run'; -const kResetText = 'Reset'; -const kClearOutputText = 'Clear Output'; -const kNewExampleText = 'New Example'; - -class ResetIntent extends Intent { - const ResetIntent(); -} - -class RunIntent extends Intent { - const RunIntent(); +class ClearOutputIntent extends BeamIntent { + const ClearOutputIntent() : super(slug: 'intents.playground.clearOutput'); } -class ClearOutputIntent extends Intent { - const ClearOutputIntent(); +class NewExampleIntent extends BeamIntent { + const NewExampleIntent() : super(slug: 'intents.playground.newExample'); } -class NewExampleIntent extends Intent { - const NewExampleIntent(); -} - -final kRunShortcut = Shortcut( - shortcuts: LogicalKeySet( - LogicalKeyboardKey.meta, - LogicalKeyboardKey.enter, - ), - actionIntent: const RunIntent(), - name: kRunText, - createAction: (BuildContext context) => CallbackAction( - onInvoke: (_) => Provider.of( - context, - listen: false, - ).runCode(), - ), -); - -final kResetShortcut = Shortcut( - shortcuts: LogicalKeySet( - LogicalKeyboardKey.meta, - LogicalKeyboardKey.shift, - LogicalKeyboardKey.keyE, - ), - actionIntent: const ResetIntent(), - name: kResetText, - createAction: (BuildContext context) => CallbackAction( - onInvoke: (_) => Provider.of( - context, - listen: false, - ).reset(), - ), -); - -final kClearOutputShortcut = Shortcut( +final kClearOutputShortcut = BeamShortcut( shortcuts: LogicalKeySet( LogicalKeyboardKey.meta, LogicalKeyboardKey.keyB, ), actionIntent: const ClearOutputIntent(), - name: kClearOutputText, createAction: (BuildContext context) => CallbackAction( - onInvoke: (_) => Provider.of( + onInvoke: (_) => Provider.of( context, listen: false, ).clearOutput(), ), ); -final kNewExampleShortcut = Shortcut( +final kNewExampleShortcut = BeamShortcut( shortcuts: LogicalKeySet( LogicalKeyboardKey.meta, LogicalKeyboardKey.keyM, ), actionIntent: const NewExampleIntent(), - name: kNewExampleText, createAction: (_) => CallbackAction( onInvoke: (_) => launchUrl(Uri.parse('/')), ), ); -final List globalShortcuts = [ - kRunShortcut, - kResetShortcut, +final List globalShortcuts = [ kClearOutputShortcut, kNewExampleShortcut, ]; diff --git a/playground/frontend/lib/pages/embedded_playground/components/embedded_actions.dart b/playground/frontend/lib/pages/embedded_playground/components/embedded_actions.dart index 5e8e56bf4faa..10ccf96ba4f8 100644 --- a/playground/frontend/lib/pages/embedded_playground/components/embedded_actions.dart +++ b/playground/frontend/lib/pages/embedded_playground/components/embedded_actions.dart @@ -27,8 +27,8 @@ import 'package:playground/constants/assets.dart'; import 'package:playground/constants/params.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/messages/models/set_content_message.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; import 'package:playground/utils/javascript_post_message.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; const kTryPlaygroundButtonWidth = 200.0; @@ -44,29 +44,29 @@ class EmbeddedActions extends StatelessWidget { child: SizedBox( width: kTryPlaygroundButtonWidth, height: kTryPlaygroundButtonHeight, - child: Consumer( - builder: (context, state, child) => ElevatedButton.icon( + child: Consumer( + builder: (context, controller, child) => ElevatedButton.icon( icon: SvgPicture.asset(kLinkIconAsset), label: Text(AppLocalizations.of(context)!.tryInPlayground), - onPressed: () => _openStandalonePlayground(state), + onPressed: () => _openStandalonePlayground(controller), ), ), ), ); } - void _openStandalonePlayground(PlaygroundState state) { + void _openStandalonePlayground(PlaygroundController controller) { // The empty list forces the parsing of EmptyExampleLoadingDescriptor // and prevents the glimpse of the default catalog example. final window = html.window.open( - '/?$kExamplesParam=[]&$kSdkParam=${state.sdk?.name}', + '/?$kExamplesParam=[]&$kSdkParam=${controller.sdk?.id}', '', ); javaScriptPostMessageRepeated( window, SetContentMessage( - descriptor: state.getLoadingDescriptor(), + descriptor: controller.getLoadingDescriptor(), ), ); } diff --git a/playground/frontend/lib/pages/embedded_playground/components/embedded_appbar_title.dart b/playground/frontend/lib/pages/embedded_playground/components/embedded_appbar_title.dart index fd981f598751..112bea826ddb 100644 --- a/playground/frontend/lib/pages/embedded_playground/components/embedded_appbar_title.dart +++ b/playground/frontend/lib/pages/embedded_playground/components/embedded_appbar_title.dart @@ -18,16 +18,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/components/toggle_theme_button/toggle_theme_icon_button.dart'; +import 'package:playground/components/playground_run_or_cancel_button.dart'; import 'package:playground/constants/assets.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:playground/modules/editor/components/run_button.dart'; -import 'package:playground/modules/notifications/components/notification.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:playground/utils/analytics_utils.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class EmbeddedAppBarTitle extends StatelessWidget { @@ -35,50 +30,19 @@ class EmbeddedAppBarTitle extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, state, child) => Wrap( + return Consumer( + builder: (context, controller, child) => Wrap( crossAxisAlignment: WrapCrossAlignment.center, spacing: kXlSpacing, children: [ - RunButton( - isRunning: state.isCodeRunning, - cancelRun: () { - final exampleName = getAnalyticsExampleName(state); - final analyticsService = AnalyticsService.get(context); - analyticsService.trackClickCancelRunEvent(exampleName); - - state.cancelRun().catchError( - (_) => NotificationManager.showError( - context, - AppLocalizations.of(context)!.runCode, - AppLocalizations.of(context)!.cancelExecution, - ), - ); - }, - runCode: () { - final stopwatch = Stopwatch()..start(); - final exampleName = getAnalyticsExampleName(state); - final analyticsService = AnalyticsService.get(context); - - state.runCode( - onFinish: () { - analyticsService.trackRunTimeEvent( - exampleName, - stopwatch.elapsedMilliseconds, - ); - }, - ); - analyticsService.trackClickRunEvent(exampleName); - }, - ), + const PlaygroundRunOrCancelButton(), const ToggleThemeIconButton(), IconButton( iconSize: kIconSizeLg, splashRadius: kIconButtonSplashRadius, icon: SvgPicture.asset(kCopyIconAsset), onPressed: () { - final source = - Provider.of(context, listen: false).source; + final source = controller.source; Clipboard.setData(ClipboardData(text: source)); }, ), diff --git a/playground/frontend/lib/pages/embedded_playground/components/embedded_editor.dart b/playground/frontend/lib/pages/embedded_playground/components/embedded_editor.dart index 9728bdb1f438..94000034f299 100644 --- a/playground/frontend/lib/pages/embedded_playground/components/embedded_editor.dart +++ b/playground/frontend/lib/pages/embedded_playground/components/embedded_editor.dart @@ -17,10 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/components/loading_indicator/loading_indicator.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/editor/components/editor_textarea.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class EmbeddedEditor extends StatelessWidget { @@ -30,21 +27,17 @@ class EmbeddedEditor extends StatelessWidget { @override Widget build(BuildContext context) { - final state = Provider.of(context); - final controller = state.snippetEditingController; + final controller = Provider.of(context); + final snippetController = controller.snippetEditingController; - if (controller == null) { - return const LoadingIndicator(size: kLgLoadingIndicatorSize); + if (snippetController == null) { + return const LoadingIndicator(); } - return EditorTextArea( - codeController: controller.codeController, - key: ValueKey(state.selectedExample), - enabled: true, - sdk: controller.sdk, - example: state.selectedExample, + return SnippetEditor( + controller: snippetController, isEditable: isEditable, - isEmbedded: true, + goToContextLine: false, ); } } diff --git a/playground/frontend/lib/pages/embedded_playground/embedded_playground_page.dart b/playground/frontend/lib/pages/embedded_playground/embedded_playground_page.dart index 8d4c41454f3c..396c31a8264d 100644 --- a/playground/frontend/lib/pages/embedded_playground/embedded_playground_page.dart +++ b/playground/frontend/lib/pages/embedded_playground/embedded_playground_page.dart @@ -17,12 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/modules/output/components/output.dart'; import 'package:playground/pages/embedded_playground/components/embedded_actions.dart'; import 'package:playground/pages/embedded_playground/components/embedded_appbar_title.dart'; import 'package:playground/pages/embedded_playground/components/embedded_editor.dart'; import 'package:playground/pages/embedded_playground/components/embedded_split_view.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; const kActionsWidth = 300.0; @@ -38,8 +37,8 @@ class EmbeddedPlaygroundPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, state, child) => Scaffold( + return Consumer( + builder: (context, controller, child) => Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: const EmbeddedAppBarTitle(), @@ -49,9 +48,9 @@ class EmbeddedPlaygroundPage extends StatelessWidget { first: EmbeddedEditor(isEditable: isEditable), second: Container( color: Theme.of(context).backgroundColor, - child: Output( - isEmbedded: true, - playgroundState: state, + child: OutputWidget( + playgroundController: controller, + graphDirection: Axis.horizontal, ), ), ), diff --git a/playground/frontend/lib/pages/playground/components/close_listener.dart b/playground/frontend/lib/pages/playground/components/close_listener.dart index 682355585b09..ba2a19a7ae22 100644 --- a/playground/frontend/lib/pages/playground/components/close_listener.dart +++ b/playground/frontend/lib/pages/playground/components/close_listener.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'dart:html' as html; import 'package:provider/provider.dart'; @@ -36,7 +36,7 @@ class _CloseListenerState extends State { void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { html.window.onBeforeUnload.listen((event) async { - Provider.of(context, listen: false).cancelRun(); + Provider.of(context, listen: false).cancelRun(); }); }); super.initState(); diff --git a/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart b/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart index be58a603d181..5d427f41d98f 100644 --- a/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart +++ b/playground/frontend/lib/pages/playground/components/editor_textarea_wrapper.dart @@ -18,17 +18,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/components/loading_indicator/loading_indicator.dart'; +import 'package:playground/components/playground_run_or_cancel_button.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/analytics/analytics_service.dart'; -import 'package:playground/modules/editor/components/editor_textarea.dart'; -import 'package:playground/modules/editor/components/run_button.dart'; import 'package:playground/modules/editor/components/share_dropdown/share_button.dart'; import 'package:playground/modules/examples/components/description_popover/description_popover_button.dart'; import 'package:playground/modules/examples/components/multifile_popover/multifile_popover_button.dart'; -import 'package:playground/modules/notifications/components/notification.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:playground/utils/analytics_utils.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class CodeTextAreaWrapper extends StatelessWidget { @@ -36,17 +31,17 @@ class CodeTextAreaWrapper extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer(builder: (context, state, child) { - if (state.result?.errorMessage?.isNotEmpty ?? false) { + return Consumer(builder: (context, controller, child) { + if (controller.result?.errorMessage?.isNotEmpty ?? false) { WidgetsBinding.instance.addPostFrameCallback((_) { - _handleError(context, state); + _handleError(context, controller); }); } - final controller = state.snippetEditingController; + final snippetController = controller.snippetEditingController; - if (controller == null) { - return const LoadingIndicator(size: kLgLoadingIndicatorSize); + if (snippetController == null) { + return const LoadingIndicator(); } return Column( @@ -55,12 +50,10 @@ class CodeTextAreaWrapper extends StatelessWidget { child: Stack( children: [ Positioned.fill( - child: EditorTextArea( - codeController: controller.codeController, - enabled: !(state.selectedExample?.isMultiFile ?? false), - example: state.selectedExample, - sdk: controller.sdk, + child: SnippetEditor( + controller: snippetController, isEditable: true, + goToContextLine: true, ), ), Positioned( @@ -69,12 +62,12 @@ class CodeTextAreaWrapper extends StatelessWidget { height: kButtonHeight, child: Row( children: [ - if (state.selectedExample != null) ...[ - if (state.selectedExample?.isMultiFile ?? false) + if (controller.selectedExample != null) ...[ + if (controller.selectedExample?.isMultiFile ?? false) Semantics( container: true, child: MultifilePopoverButton( - example: state.selectedExample!, + example: controller.selectedExample!, followerAnchor: Alignment.topRight, targetAnchor: Alignment.bottomRight, ), @@ -82,7 +75,7 @@ class CodeTextAreaWrapper extends StatelessWidget { Semantics( container: true, child: DescriptionPopoverButton( - example: state.selectedExample!, + example: controller.selectedExample!, followerAnchor: Alignment.topRight, targetAnchor: Alignment.bottomRight, ), @@ -95,39 +88,7 @@ class CodeTextAreaWrapper extends StatelessWidget { const SizedBox(width: kLgSpacing), Semantics( container: true, - child: RunButton( - disabled: state.selectedExample?.isMultiFile ?? false, - isRunning: state.isCodeRunning, - cancelRun: () { - final exampleName = getAnalyticsExampleName(state); - AnalyticsService.get(context) - .trackClickCancelRunEvent(exampleName); - state.cancelRun().catchError( - (_) => NotificationManager.showError( - context, - AppLocalizations.of(context)!.runCode, - AppLocalizations.of(context)! - .cancelExecution, - ), - ); - }, - runCode: () { - AnalyticsService analyticsService = - AnalyticsService.get(context); - final stopwatch = Stopwatch()..start(); - final exampleName = getAnalyticsExampleName(state); - - state.runCode( - onFinish: () { - analyticsService.trackRunTimeEvent( - exampleName, - stopwatch.elapsedMilliseconds, - ); - }, - ); - analyticsService.trackClickRunEvent(exampleName); - }, - ), + child: const PlaygroundRunOrCancelButton(), ), ], ), @@ -140,12 +101,12 @@ class CodeTextAreaWrapper extends StatelessWidget { }); } - _handleError(BuildContext context, PlaygroundState state) { + void _handleError(BuildContext context, PlaygroundController controller) { NotificationManager.showError( context, AppLocalizations.of(context)!.runCode, - state.result?.errorMessage ?? '', + controller.result?.errorMessage ?? '', ); - state.resetError(); + controller.resetError(); } } diff --git a/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart b/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart index 38f17396cbff..278d973d84ef 100644 --- a/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart +++ b/playground/frontend/lib/pages/playground/components/feedback/feedback_dropdown_content.dart @@ -17,13 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/components/horizontal_divider/horizontal_divider.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/fonts.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; import 'package:playground/pages/playground/components/feedback/feedback_dropdown_icon_button.dart'; +import 'package:playground_components/playground_components.dart'; const double kTextFieldWidth = 365.0; const double kTextFieldHeight = 68.0; @@ -46,8 +45,10 @@ class FeedbackDropdownContent extends StatelessWidget { @override Widget build(BuildContext context) { + final borderColor = Theme.of(context).extension()!.borderColor; + final OutlineInputBorder border = OutlineInputBorder( - borderSide: BorderSide(color: ThemeColors.of(context).lightGreyColor), + borderSide: BorderSide(color: borderColor), borderRadius: BorderRadius.circular(kMdBorderRadius), ); @@ -114,7 +115,7 @@ class FeedbackDropdownContent extends StatelessWidget { enabledBorder: border, contentPadding: const EdgeInsets.all(kMdSpacing), ), - cursorColor: ThemeColors.of(context).lightGreyColor, + cursorColor: borderColor, cursorWidth: kCursorSize, onFieldSubmitted: (String filterText) {}, maxLines: 3, @@ -124,7 +125,7 @@ class FeedbackDropdownContent extends StatelessWidget { ], ), ), - const HorizontalDivider(), + const BeamDivider(), Padding( padding: const EdgeInsets.only( top: kXlSpacing, @@ -141,7 +142,7 @@ class FeedbackDropdownContent extends StatelessWidget { color: Theme.of(context).backgroundColor, borderRadius: BorderRadius.circular(kSmBorderRadius), border: Border.all( - color: ThemeColors.of(context).lightGreyColor, + color: borderColor, ), ), child: TextButton( diff --git a/playground/frontend/lib/pages/playground/components/more_actions.dart b/playground/frontend/lib/pages/playground/components/more_actions.dart index 4fe7c9aa121c..60f4e0ff9b34 100644 --- a/playground/frontend/lib/pages/playground/components/more_actions.dart +++ b/playground/frontend/lib/pages/playground/components/more_actions.dart @@ -19,11 +19,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/assets.dart'; import 'package:playground/constants/links.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; import 'package:playground/modules/shortcuts/components/shortcuts_modal.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:url_launcher/url_launcher.dart'; enum HeaderAction { @@ -36,7 +36,11 @@ enum HeaderAction { } class MoreActions extends StatefulWidget { - const MoreActions({Key? key}) : super(key: key); + final PlaygroundController playgroundController; + + const MoreActions({ + required this.playgroundController, + }); @override State createState() => _MoreActionsState(); @@ -52,7 +56,7 @@ class _MoreActionsState extends State { child: PopupMenuButton( icon: Icon( Icons.more_horiz_outlined, - color: ThemeColors.of(context).grey1Color, + color: Theme.of(context).extension()?.iconColor, ), itemBuilder: (BuildContext context) => >[ PopupMenuItem( @@ -62,10 +66,12 @@ class _MoreActionsState extends State { leading: SvgPicture.asset(kShortcutsIconAsset), title: Text(appLocale.shortcuts), onTap: () { - AnalyticsService.get(context).trackOpenShortcutsModal(); - showDialog( + AnalyticsService.get(context).trackOpenShortcutsModal(); + showDialog( context: context, - builder: (BuildContext context) => const ShortcutsModal(), + builder: (BuildContext context) => ShortcutsModal( + playgroundController: widget.playgroundController, + ), ); }, ), diff --git a/playground/frontend/lib/pages/playground/components/playground_page_body.dart b/playground/frontend/lib/pages/playground/components/playground_page_body.dart index 8a723982ed40..419b44dca5df 100644 --- a/playground/frontend/lib/pages/playground/components/playground_page_body.dart +++ b/playground/frontend/lib/pages/playground/components/playground_page_body.dart @@ -17,14 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/components/split_view/split_view.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/output/components/output.dart'; +import 'package:playground/modules/output/components/output_header/output_placements.dart'; import 'package:playground/modules/output/models/output_placement.dart'; import 'package:playground/modules/output/models/output_placement_state.dart'; import 'package:playground/pages/playground/components/editor_textarea_wrapper.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class PlaygroundPageBody extends StatelessWidget { @@ -32,30 +30,35 @@ class PlaygroundPageBody extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer2( + return Consumer2( builder: (context, outputState, playgroundState, child) { - final output = createOutput(playgroundState); + final output = OutputWidget( + // isEmbedded: false, + graphDirection: outputState.placement.graphDirection, + playgroundController: playgroundState, + trailing: const OutputPlacements(), + ); + switch (outputState.placement) { case OutputPlacement.bottom: return SplitView( - direction: SplitViewDirection.vertical, + direction: Axis.vertical, first: codeTextArea, second: output, - dividerSize: kMdSpacing, ); + case OutputPlacement.left: return SplitView( - direction: SplitViewDirection.horizontal, + direction: Axis.horizontal, first: output, second: codeTextArea, - dividerSize: kMdSpacing, ); + case OutputPlacement.right: return SplitView( - direction: SplitViewDirection.horizontal, + direction: Axis.horizontal, first: codeTextArea, second: output, - dividerSize: kMdSpacing, ); } }); @@ -63,18 +66,13 @@ class PlaygroundPageBody extends StatelessWidget { Widget get codeTextArea => const CodeTextAreaWrapper(); - Widget createOutput(PlaygroundState state) => Output( - isEmbedded: false, - playgroundState: state, - ); - Widget getVerticalSeparator(BuildContext context) => Container( width: kMdSpacing, - color: ThemeColors.of(context).divider, + color: Theme.of(context).dividerColor, ); Widget getHorizontalSeparator(BuildContext context) => Container( height: kMdSpacing, - color: ThemeColors.of(context).divider, + color: Theme.of(context).dividerColor, ); } diff --git a/playground/frontend/lib/pages/playground/components/playground_page_footer.dart b/playground/frontend/lib/pages/playground/components/playground_page_footer.dart index 7e6b6bcf799a..b97bafad949f 100644 --- a/playground/frontend/lib/pages/playground/components/playground_page_footer.dart +++ b/playground/frontend/lib/pages/playground/components/playground_page_footer.dart @@ -18,12 +18,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/constants/font_weight.dart'; import 'package:playground/constants/links.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; import 'package:playground/pages/playground/components/feedback/playground_feedback.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:url_launcher/url_launcher.dart'; class PlaygroundPageFooter extends StatelessWidget { @@ -34,7 +34,9 @@ class PlaygroundPageFooter extends StatelessWidget { AppLocalizations appLocale = AppLocalizations.of(context)!; return Container( - color: ThemeColors.of(context).secondaryBackground, + color: Theme.of(context) + .extension() + ?.secondaryBackgroundColor, width: double.infinity, child: Padding( padding: const EdgeInsets.symmetric( diff --git a/playground/frontend/lib/pages/playground/components/playground_page_providers.dart b/playground/frontend/lib/pages/playground/components/playground_page_providers.dart index 9b809c9c262c..69c2713e30f1 100644 --- a/playground/frontend/lib/pages/playground/components/playground_page_providers.dart +++ b/playground/frontend/lib/pages/playground/components/playground_page_providers.dart @@ -17,26 +17,18 @@ */ import 'package:flutter/material.dart'; +import 'package:playground/config.g.dart'; import 'package:playground/modules/analytics/analytics_service.dart'; import 'package:playground/modules/analytics/google_analytics_service.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/grpc_code_client.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_repository.dart'; -import 'package:playground/modules/examples/repositories/example_client/grpc_example_client.dart'; -import 'package:playground/modules/examples/repositories/example_repository.dart'; +import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart'; import 'package:playground/modules/messages/handlers/messages_debouncer.dart'; import 'package:playground/modules/messages/handlers/messages_handler.dart'; import 'package:playground/modules/messages/listeners/messages_listener.dart'; import 'package:playground/modules/output/models/output_placement_state.dart'; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; import 'package:playground/pages/playground/states/feedback_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; -final CodeRepository kCodeRepository = CodeRepository(GrpcCodeClient()); -final ExampleRepository kExampleRepository = - ExampleRepository(GrpcExampleClient()); - class PlaygroundPageProviders extends StatelessWidget { final Widget child; @@ -52,20 +44,47 @@ class PlaygroundPageProviders extends StatelessWidget { Provider( create: (context) => GoogleAnalyticsService(), ), - ChangeNotifierProvider( + ChangeNotifierProvider( create: (context) { - final state = PlaygroundState( + final codeRepository = CodeRepository( + client: GrpcCodeClient( + url: kApiClientURL, + runnerUrlsById: { + Sdk.java.id: kApiJavaClientURL, + Sdk.go.id: kApiGoClientURL, + Sdk.python.id: kApiPythonClientURL, + Sdk.scio.id: kApiScioClientURL, + }, + ), + ); + + final exampleRepository = ExampleRepository( + client: GrpcExampleClient(url: kApiClientURL), + ); + + final exampleCache = ExampleCache( + exampleRepository: exampleRepository, + hasCatalog: true, + )..init(); + + final controller = PlaygroundController( examplesLoader: ExamplesLoader(), - exampleState: ExampleState(kExampleRepository)..init(), - codeRepository: kCodeRepository, + exampleCache: exampleCache, + codeRepository: codeRepository, + ); + + final descriptor = ExamplesLoadingDescriptorFactory.fromUriParts( + path: Uri.base.path, + params: Uri.base.queryParameters, ); + controller.examplesLoader.load(descriptor); final handler = MessagesDebouncer( - handler: MessagesHandler(playgroundState: state), + handler: MessagesHandler(playgroundController: controller), ); MessagesListener(handler: handler); - return state; + return controller; }, ), ChangeNotifierProvider( diff --git a/playground/frontend/lib/pages/playground/playground_page.dart b/playground/frontend/lib/pages/playground/playground_page.dart index 7f68817f3e4a..a7a66f02d5ab 100644 --- a/playground/frontend/lib/pages/playground/playground_page.dart +++ b/playground/frontend/lib/pages/playground/playground_page.dart @@ -18,7 +18,6 @@ import 'package:flutter/material.dart'; import 'package:playground/components/logo/logo_component.dart'; -import 'package:playground/components/toggle_theme_button/toggle_theme_button.dart'; import 'package:playground/constants/sizes.dart'; import 'package:playground/modules/actions/components/new_example_action.dart'; import 'package:playground/modules/actions/components/reset_action.dart'; @@ -33,7 +32,7 @@ import 'package:playground/pages/playground/components/close_listener_nonweb.dar import 'package:playground/pages/playground/components/more_actions.dart'; import 'package:playground/pages/playground/components/playground_page_body.dart'; import 'package:playground/pages/playground/components/playground_page_footer.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class PlaygroundPage extends StatelessWidget { @@ -42,56 +41,65 @@ class PlaygroundPage extends StatelessWidget { @override Widget build(BuildContext context) { return CloseListener( - child: ShortcutsManager( - shortcuts: globalShortcuts, - child: Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - title: Consumer( - builder: (context, state, child) { - final controller = state.snippetEditingController; + child: Consumer( + builder: (context, controller, child) { + final snippetController = controller.snippetEditingController; - return Wrap( + return ShortcutsManager( + shortcuts: [ + ...controller.shortcuts, + ...globalShortcuts, + ], + child: Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Wrap( crossAxisAlignment: WrapCrossAlignment.center, spacing: kXlSpacing, children: [ const Logo(), ExampleSelector( changeSelectorVisibility: - state.exampleState.changeSelectorVisibility, - isSelectorOpened: state.exampleState.isSelectorOpened, + controller.exampleCache.changeSelectorVisibility, + isSelectorOpened: + controller.exampleCache.isSelectorOpened, ), SDKSelector( - value: state.sdk, + value: controller.sdk, onChanged: (newSdk) { AnalyticsService.get(context) - .trackSelectSdk(state.sdk, newSdk); - state.setSdk(newSdk); + .trackSelectSdk(controller.sdk, newSdk); + controller.setSdk(newSdk); }, ), - if (controller != null) + if (snippetController != null) PipelineOptionsDropdown( - pipelineOptions: controller.pipelineOptions, - setPipelineOptions: state.setPipelineOptions, + pipelineOptions: snippetController.pipelineOptions, + setPipelineOptions: controller.setPipelineOptions, ), const NewExampleAction(), - ResetAction(reset: state.reset), + const ResetAction(), ], - ); - }, - ), - actions: const [ToggleThemeButton(), MoreActions()], - ), - body: Column( - children: [ - const Expanded(child: PlaygroundPageBody()), - Semantics( - container: true, - child: const PlaygroundPageFooter(), + ), + actions: [ + const ToggleThemeButton(), + MoreActions( + playgroundController: controller, + ), + ], ), - ], - ), - ), + body: Column( + children: [ + const Expanded(child: PlaygroundPageBody()), + Semantics( + container: true, + child: const PlaygroundPageFooter(), + ), + ], + ), + ), + ); + }, ), ); } diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart b/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart deleted file mode 100644 index ad4efacd1309..000000000000 --- a/playground/frontend/lib/pages/playground/states/example_loaders/examples_loader.dart +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart'; -import 'package:playground/pages/playground/states/example_loaders/catalog_default_example_loader.dart'; -import 'package:playground/pages/playground/states/example_loaders/content_example_loader.dart'; -import 'package:playground/pages/playground/states/example_loaders/empty_example_loader.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; -import 'package:playground/pages/playground/states/example_loaders/standard_example_loader.dart'; -import 'package:playground/pages/playground/states/example_loaders/user_shared_example_loader.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; - -class ExamplesLoader { - PlaygroundState? _playgroundState; - ExamplesLoadingDescriptor? _descriptor; - - void setPlaygroundState(PlaygroundState value) { - _playgroundState = value; - } - - Future load(ExamplesLoadingDescriptor descriptor) async { - if (_descriptor == descriptor) { - return; - } - - _descriptor = descriptor; - await Future.wait( - descriptor.descriptors.map( - (one) => loadOne(group: descriptor, one: one), - ), - ); - - final sdk = descriptor.initialSdk; - if (sdk != null) { - _playgroundState!.setSdk(sdk); - } - } - - Future loadOne({ - required ExamplesLoadingDescriptor group, - required ExampleLoadingDescriptor one, - }) async { - final example = await _getOneLoader(one).future; - _playgroundState!.setExample( - example, - setCurrentSdk: - example.sdk == group.initialSdk || group.initialSdk == null, - ); - } - - ExampleLoader _getOneLoader(ExampleLoadingDescriptor descriptor) { - final exampleState = _playgroundState!.exampleState; - - if (descriptor is CatalogDefaultExampleLoadingDescriptor) { - return CatalogDefaultExampleLoader( - descriptor: descriptor, - exampleState: exampleState, - ); - } - - if (descriptor is ContentExampleLoadingDescriptor) { - return ContentExampleLoader( - descriptor: descriptor, - ); - } - - if (descriptor is EmptyExampleLoadingDescriptor) { - return EmptyExampleLoader( - descriptor: descriptor, - ); - } - - if (descriptor is StandardExampleLoadingDescriptor) { - return StandardExampleLoader( - descriptor: descriptor, - exampleState: exampleState, - ); - } - - if (descriptor is UserSharedExampleLoadingDescriptor) { - return UserSharedExampleLoader( - descriptor: descriptor, - exampleState: exampleState, - ); - } - - throw Exception('Unknown example loading descriptor: $descriptor'); - } -} diff --git a/playground/frontend/lib/pages/playground/states/example_selector_state.dart b/playground/frontend/lib/pages/playground/states/example_selector_state.dart index 2f6f462f7f01..f62a1773012f 100644 --- a/playground/frontend/lib/pages/playground/states/example_selector_state.dart +++ b/playground/frontend/lib/pages/playground/states/example_selector_state.dart @@ -17,18 +17,16 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; class ExampleSelectorState with ChangeNotifier { - final PlaygroundState _playgroundState; + final PlaygroundController _playgroundController; ExampleType _selectedFilterType; String _filterText; - List categories; + List categories; ExampleSelectorState( - this._playgroundState, + this._playgroundController, this.categories, [ this._selectedFilterType = ExampleType.all, this._filterText = '', @@ -48,26 +46,26 @@ class ExampleSelectorState with ChangeNotifier { notifyListeners(); } - void setCategories(List? categories) { - this.categories = categories ?? []; + void setCategories(List categories) { + this.categories = categories; notifyListeners(); } void sortCategories() { - final categories = _playgroundState.exampleState.getCategories( - _playgroundState.sdk, + final categories = _playgroundController.exampleCache.getCategories( + _playgroundController.sdk, ); final sortedCategories = categories - .map((category) => CategoryModel( - name: category.name, + .map((category) => CategoryWithExamples( + title: category.title, examples: _sortCategoryExamples(category.examples))) .where((category) => category.examples.isNotEmpty) .toList(); setCategories(sortedCategories); } - List _sortCategoryExamples(List examples) { + List _sortCategoryExamples(List examples) { final isAllFilterType = selectedFilterType == ExampleType.all; final isFilterTextEmpty = filterText.isEmpty; if (isAllFilterType && isFilterTextEmpty) { @@ -89,15 +87,15 @@ class ExampleSelectorState with ChangeNotifier { return sortExamplesByName(sorted, filterText); } - List sortExamplesByType( - List examples, + List sortExamplesByType( + List examples, ExampleType type, ) { return examples.where((element) => element.type == type).toList(); } - List sortExamplesByName( - List examples, + List sortExamplesByName( + List examples, String name, ) { return examples diff --git a/playground/frontend/lib/pages/playground/states/examples_state.dart b/playground/frontend/lib/pages/playground/states/examples_state.dart deleted file mode 100644 index 594e74a7a1b0..000000000000 --- a/playground/frontend/lib/pages/playground/states/examples_state.dart +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:playground/constants/params.dart'; -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/examples/repositories/example_repository.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart'; -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -class ExampleState with ChangeNotifier { - final ExampleRepository _exampleRepository; - Map>? sdkCategories; - Map defaultExamplesMap = {}; - ExampleModel? defaultExample; - bool isSelectorOpened = false; - - final _allExamplesCompleter = Completer(); - - Future get allExamplesFuture => _allExamplesCompleter.future; - - bool get hasExampleCatalog => !isEmbedded(); - - ExampleState(this._exampleRepository); - - Future init() async { - if (hasExampleCatalog) { - await Future.wait([ - _loadCategories(), - loadDefaultExamplesIfNot(), - ]); - } - } - - void setSdkCategories(Map> map) { - sdkCategories = map; - _allExamplesCompleter.complete(); - } - - List getCategories(SDK? sdk) { - return sdkCategories?[sdk] ?? []; - } - - Future getExampleOutput(String id, SDK sdk) async { - return _exampleRepository.getExampleOutput( - GetExampleRequestWrapper(id, sdk), - ); - } - - Future getExampleSource(String id, SDK sdk) async { - return _exampleRepository.getExampleSource( - GetExampleRequestWrapper(id, sdk), - ); - } - - Future getExample(String path, SDK sdk) async { - return _exampleRepository.getExample( - GetExampleRequestWrapper(path, sdk), - ); - } - - Future getExampleLogs(String id, SDK sdk) async { - return _exampleRepository.getExampleLogs( - GetExampleRequestWrapper(id, sdk), - ); - } - - Future getExampleGraph(String id, SDK sdk) async { - return _exampleRepository.getExampleGraph( - GetExampleRequestWrapper(id, sdk), - ); - } - - Future loadSharedExample(String id) async { - GetSnippetResponse result = await _exampleRepository.getSnippet( - GetSnippetRequestWrapper(id: id), - ); - return ExampleModel( - sdk: result.sdk, - name: result.files.first.name, - path: id, - description: '', - type: ExampleType.example, - source: result.files.first.code, - pipelineOptions: result.pipelineOptions, - ); - } - - Future getSnippetId({ - required List files, - required SDK sdk, - required String pipelineOptions, - }) async { - String id = await _exampleRepository.saveSnippet(SaveSnippetRequestWrapper( - files: files, - sdk: sdk, - pipelineOptions: pipelineOptions, - )); - return id; - } - - Future loadExampleInfo(ExampleModel example) async { - if (example.isInfoFetched()) { - return example; - } - - //GRPC GetPrecompiledGraph errors hotfix - if (example.name == 'MinimalWordCount' && - (example.sdk == SDK.go || example.sdk == SDK.scio)) { - final exampleData = await Future.wait([ - getExampleSource(example.path, example.sdk), - getExampleOutput(example.path, example.sdk), - getExampleLogs(example.path, example.sdk), - ]); - example.setSource(exampleData[0]); - example.setOutputs(exampleData[1]); - example.setLogs(exampleData[2]); - return example; - } - - final exampleData = await Future.wait([ - getExampleSource(example.path, example.sdk), - getExampleOutput(example.path, example.sdk), - getExampleLogs(example.path, example.sdk), - getExampleGraph(example.path, example.sdk) - ]); - example.setSource(exampleData[0]); - example.setOutputs(exampleData[1]); - example.setLogs(exampleData[2]); - example.setGraph(exampleData[3]); - return example; - } - - Future _loadCategories() { - return _exampleRepository - .getListOfExamples( - GetListOfExamplesRequestWrapper(sdk: null, category: null), - ) - .then((map) => setSdkCategories(map)); - } - - void changeSelectorVisibility() { - isSelectorOpened = !isSelectorOpened; - notifyListeners(); - } - - Future loadDefaultExamples() async { - if (defaultExamplesMap.isNotEmpty) { - return; - } - - try { - await Future.wait(SDK.values.map(_loadDefaultExample)); - } catch (ex) { - if (defaultExamplesMap.isEmpty) { - rethrow; - } - // As long as any of the examples is loaded, continue. - print(ex); - // TODO: Log. - } - - notifyListeners(); - } - - Future _loadDefaultExample(SDK sdk) async { - final exampleWithoutInfo = await _exampleRepository.getDefaultExample( - // First parameter is an empty string, because we don't need path to get the default example. - GetExampleRequestWrapper('', sdk), - ); - - defaultExamplesMap[sdk] = await loadExampleInfo(exampleWithoutInfo); - } - - Future loadDefaultExamplesIfNot() async { - if (defaultExamplesMap.isNotEmpty) { - return; - } - - await loadDefaultExamples(); - } - - Future getCatalogExampleByPath(String path) async { - await allExamplesFuture; - - final allExamples = sdkCategories?.values - .expand((sdkCategory) => sdkCategory.map((e) => e.examples)) - .expand((element) => element); - - return allExamples?.firstWhereOrNull( - (e) => e.path == path, - ); - } -} diff --git a/playground/frontend/lib/playground_app.dart b/playground/frontend/lib/playground_app.dart index 8c5f2a489e6e..867bbad7f4b9 100644 --- a/playground/frontend/lib/playground_app.dart +++ b/playground/frontend/lib/playground_app.dart @@ -16,16 +16,16 @@ * limitations under the License. */ -import 'package:code_text_field/code_text_field.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:playground/config/locale.dart'; -import 'package:playground/config/theme.dart'; import 'package:playground/l10n/l10n.dart'; import 'package:playground/pages/playground/components/playground_page_providers.dart'; import 'package:playground/pages/playground/playground_page.dart'; import 'package:playground/pages/routes.dart'; +import 'package:playground_components/playground_components.dart'; import 'package:provider/provider.dart'; class PlaygroundApp extends StatelessWidget { @@ -36,32 +36,30 @@ class PlaygroundApp extends StatelessWidget { return ThemeSwitchNotifierProvider( child: Consumer( builder: (context, themeSwitchNotifier, _) { - return CodeTheme( - data: themeSwitchNotifier.codeTheme, - child: ChangeNotifierProvider( - create: (context) => LocaleProvider(), - builder: (context, state) { - final localeProvider = Provider.of(context); - return PlaygroundPageProviders( - child: MaterialApp( - title: 'Apache Beam Playground', - themeMode: themeSwitchNotifier.themeMode, - theme: kLightTheme, - darkTheme: kDarkTheme, - onGenerateRoute: Routes.generateRoute, - home: const PlaygroundPage(), - debugShowCheckedModeBanner: false, - locale: localeProvider.locale, - supportedLocales: L10n.locales, - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - ), - ); - }, - ), + return ChangeNotifierProvider( + create: (context) => LocaleProvider(), + builder: (context, state) { + final localeProvider = Provider.of(context); + return PlaygroundPageProviders( + child: MaterialApp( + title: 'Apache Beam Playground', + themeMode: themeSwitchNotifier.themeMode, + theme: kLightTheme, + darkTheme: kDarkTheme, + onGenerateRoute: Routes.generateRoute, + home: const PlaygroundPage(), + debugShowCheckedModeBanner: false, + locale: localeProvider.locale, + supportedLocales: L10n.locales, + localizationsDelegates: [ + ...context.localizationDelegates, + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + ), + ); + }, ); }, ), diff --git a/playground/frontend/lib/utils/analytics_utils.dart b/playground/frontend/lib/utils/analytics_utils.dart index ab77fc23d964..786b54ff382f 100644 --- a/playground/frontend/lib/utils/analytics_utils.dart +++ b/playground/frontend/lib/utils/analytics_utils.dart @@ -16,13 +16,12 @@ * limitations under the License. */ -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/playground_components.dart'; -String getAnalyticsExampleName(PlaygroundState state) { - final customCodeName = 'Custom code, sdk ${state.sdk?.displayName}'; - if (state.isExampleChanged) { +String getAnalyticsExampleName(PlaygroundController controller) { + final customCodeName = 'Custom code, sdk ${controller.sdk?.title}'; + if (controller.isExampleChanged) { return customCodeName; } - return state.selectedExample?.path ?? customCodeName; + return controller.selectedExample?.path ?? customCodeName; } diff --git a/playground/frontend/assets/reset.svg b/playground/frontend/playground_components/assets/buttons/reset.svg similarity index 100% rename from playground/frontend/assets/reset.svg rename to playground/frontend/playground_components/assets/buttons/reset.svg diff --git a/learning/tour-of-beam/frontend/assets/svg/theme-mode.svg b/playground/frontend/playground_components/assets/buttons/theme-mode.svg similarity index 100% rename from learning/tour-of-beam/frontend/assets/svg/theme-mode.svg rename to playground/frontend/playground_components/assets/buttons/theme-mode.svg diff --git a/playground/frontend/assets/error_notification.svg b/playground/frontend/playground_components/assets/notification_icons/error.svg similarity index 100% rename from playground/frontend/assets/error_notification.svg rename to playground/frontend/playground_components/assets/notification_icons/error.svg diff --git a/playground/frontend/assets/info_notification.svg b/playground/frontend/playground_components/assets/notification_icons/info.svg similarity index 100% rename from playground/frontend/assets/info_notification.svg rename to playground/frontend/playground_components/assets/notification_icons/info.svg diff --git a/playground/frontend/assets/success_notification.svg b/playground/frontend/playground_components/assets/notification_icons/success.svg similarity index 100% rename from playground/frontend/assets/success_notification.svg rename to playground/frontend/playground_components/assets/notification_icons/success.svg diff --git a/playground/frontend/assets/warning_notification.svg b/playground/frontend/playground_components/assets/notification_icons/warning.svg similarity index 100% rename from playground/frontend/assets/warning_notification.svg rename to playground/frontend/playground_components/assets/notification_icons/warning.svg diff --git a/playground/frontend/playground_components/assets/svg/drag.svg b/playground/frontend/playground_components/assets/svg/drag-horizontal.svg similarity index 86% rename from playground/frontend/playground_components/assets/svg/drag.svg rename to playground/frontend/playground_components/assets/svg/drag-horizontal.svg index 6ed7dd99200b..f5e8dcda558a 100644 --- a/playground/frontend/playground_components/assets/svg/drag.svg +++ b/playground/frontend/playground_components/assets/svg/drag-horizontal.svg @@ -17,7 +17,7 @@ under the License. --> - - - + + + diff --git a/playground/frontend/playground_components/assets/svg/drag-vertical.svg b/playground/frontend/playground_components/assets/svg/drag-vertical.svg new file mode 100644 index 000000000000..fea5377776ef --- /dev/null +++ b/playground/frontend/playground_components/assets/svg/drag-vertical.svg @@ -0,0 +1,23 @@ + + + + + + diff --git a/playground/frontend/playground_components/assets/svg/theme-mode.svg b/playground/frontend/playground_components/assets/svg/theme-mode.svg deleted file mode 100644 index fc1438aecf32..000000000000 --- a/playground/frontend/playground_components/assets/svg/theme-mode.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/playground/frontend/playground_components/assets/translations/en.yaml b/playground/frontend/playground_components/assets/translations/en.yaml index 9fa82e84b45d..f80677c598a9 100644 --- a/playground/frontend/playground_components/assets/translations/en.yaml +++ b/playground/frontend/playground_components/assets/translations/en.yaml @@ -18,3 +18,35 @@ ui: darkMode: 'Dark Mode' lightMode: 'Light Mode' + +intents: + playground: + run: 'Run Code' + reset: 'Reset Code' + +widgets: + + codeEditor: + label: 'Code Text Area' + + output: + filter: + all: 'All' + log: 'Log' + output: 'Output' + filterTitle: 'Display at this tab' + graph: 'Graph' + result: 'Result' + + + resetButton: + label: 'Reset' + + runOrCancelButton: + titles: + run: 'Run' + cancel: 'Cancel' + notificationTitles: + run: 'Run Code' + cancelExecution: 'Cancel Execution' + diff --git a/playground/frontend/playground_components/lib/playground_components.dart b/playground/frontend/playground_components/lib/playground_components.dart index 21338ebf9780..a7201940a84a 100644 --- a/playground/frontend/playground_components/lib/playground_components.dart +++ b/playground/frontend/playground_components/lib/playground_components.dart @@ -16,16 +16,58 @@ * limitations under the License. */ +export 'src/cache/example_cache.dart'; + export 'src/constants/colors.dart'; export 'src/constants/links.dart'; +export 'src/constants/playground_components.dart'; export 'src/constants/sizes.dart'; + +export 'src/controllers/example_loaders/examples_loader.dart'; +export 'src/controllers/playground_controller.dart'; + export 'src/enums/complexity.dart'; -export 'src/theme/color_provider.dart'; + +export 'src/models/category_with_examples.dart'; +export 'src/models/example.dart'; +export 'src/models/example_base.dart'; +export 'src/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/content_example_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/example_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/examples_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/standard_example_loading_descriptor.dart'; +export 'src/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart'; +export 'src/models/intents.dart'; +export 'src/models/outputs.dart'; +export 'src/models/sdk.dart'; +export 'src/models/shortcut.dart'; + +export 'src/notifications/notification.dart'; + +export 'src/repositories/code_client/grpc_code_client.dart'; +export 'src/repositories/code_repository.dart'; +export 'src/repositories/example_client/grpc_example_client.dart'; +export 'src/repositories/example_repository.dart'; + export 'src/theme/switch_notifier.dart'; export 'src/theme/theme.dart'; + +export 'src/util/pipeline_options.dart'; + +export 'src/widgets/bubble.dart'; export 'src/widgets/complexity.dart'; export 'src/widgets/dismissible_overlay.dart'; export 'src/widgets/divider.dart'; -export 'src/widgets/drag_indicator.dart'; +export 'src/widgets/header_icon_button.dart'; +export 'src/widgets/loading_indicator.dart'; export 'src/widgets/logo.dart'; +export 'src/widgets/output/output.dart'; +export 'src/widgets/reset_button.dart'; +export 'src/widgets/run_or_cancel_button.dart'; +export 'src/widgets/shortcut_tooltip.dart'; +export 'src/widgets/snippet_editor.dart'; +export 'src/widgets/split_view.dart'; +export 'src/widgets/tab_header.dart'; export 'src/widgets/toggle_theme_button.dart'; +export 'src/widgets/toggle_theme_icon_button.dart'; diff --git a/playground/frontend/playground_components/lib/src/constants/names.dart b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel.dart similarity index 88% rename from playground/frontend/playground_components/lib/src/constants/names.dart rename to playground/frontend/playground_components/lib/src/api/iis_workaround_channel.dart index 346d03dba5c3..5c41c00d327e 100644 --- a/playground/frontend/playground_components/lib/src/constants/names.dart +++ b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel.dart @@ -16,6 +16,5 @@ * limitations under the License. */ -class BeamNames { - static const package = 'playground_components'; -} +export 'iis_workaround_channel_non_web.dart' + if (dart.library.html) 'iis_workaround_channel_web.dart'; diff --git a/playground/frontend/playground_components/lib/src/api/iis_workaround_channel_non_web.dart b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel_non_web.dart new file mode 100644 index 000000000000..39fb9d0ba388 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel_non_web.dart @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:grpc/grpc_connection_interface.dart'; + +class IisWorkaroundChannel extends ClientChannelBase { + final Uri uri; + + IisWorkaroundChannel.xhr(this.uri) : super(); + + @override + ClientConnection createConnection() { + throw UnimplementedError('This only works in web'); + } +} diff --git a/playground/frontend/lib/api/iis_workaround_channel.dart b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel_web.dart similarity index 88% rename from playground/frontend/lib/api/iis_workaround_channel.dart rename to playground/frontend/playground_components/lib/src/api/iis_workaround_channel_web.dart index 2232c59e5034..b666677a643d 100644 --- a/playground/frontend/lib/api/iis_workaround_channel.dart +++ b/playground/frontend/playground_components/lib/src/api/iis_workaround_channel_web.dart @@ -28,12 +28,12 @@ class IisWorkaroundChannel extends ClientChannelBase { @override ClientConnection createConnection() { - return IisClientConnection(uri); + return _IisClientConnection(uri); } } -class IisClientConnection extends XhrClientConnection { - IisClientConnection(Uri uri) : super(uri); +class _IisClientConnection extends XhrClientConnection { + _IisClientConnection(super.uri); @override GrpcTransportStream makeRequest( @@ -43,7 +43,8 @@ class IisClientConnection extends XhrClientConnection { ErrorHandler onError, { CallOptions? callOptions, }) { - var pathWithoutFirstSlash = path.substring(1); + final pathWithoutFirstSlash = path.substring(1); + return super.makeRequest( pathWithoutFirstSlash, timeout, diff --git a/playground/frontend/lib/api/v1/api.pb.dart b/playground/frontend/playground_components/lib/src/api/v1/api.pb.dart similarity index 100% rename from playground/frontend/lib/api/v1/api.pb.dart rename to playground/frontend/playground_components/lib/src/api/v1/api.pb.dart diff --git a/playground/frontend/lib/api/v1/api.pbenum.dart b/playground/frontend/playground_components/lib/src/api/v1/api.pbenum.dart similarity index 100% rename from playground/frontend/lib/api/v1/api.pbenum.dart rename to playground/frontend/playground_components/lib/src/api/v1/api.pbenum.dart diff --git a/playground/frontend/lib/api/v1/api.pbgrpc.dart b/playground/frontend/playground_components/lib/src/api/v1/api.pbgrpc.dart similarity index 100% rename from playground/frontend/lib/api/v1/api.pbgrpc.dart rename to playground/frontend/playground_components/lib/src/api/v1/api.pbgrpc.dart diff --git a/playground/frontend/lib/api/v1/api.pbjson.dart b/playground/frontend/playground_components/lib/src/api/v1/api.pbjson.dart similarity index 100% rename from playground/frontend/lib/api/v1/api.pbjson.dart rename to playground/frontend/playground_components/lib/src/api/v1/api.pbjson.dart diff --git a/playground/frontend/playground_components/lib/src/cache/example_cache.dart b/playground/frontend/playground_components/lib/src/cache/example_cache.dart new file mode 100644 index 000000000000..ea755ba5949a --- /dev/null +++ b/playground/frontend/playground_components/lib/src/cache/example_cache.dart @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +import '../models/category_with_examples.dart'; +import '../models/example.dart'; +import '../models/example_base.dart'; +import '../models/sdk.dart'; +import '../repositories/example_repository.dart'; +import '../repositories/models/get_default_precompiled_object_request.dart'; +import '../repositories/models/get_precompiled_object_request.dart'; +import '../repositories/models/get_precompiled_objects_request.dart'; +import '../repositories/models/shared_file.dart'; +import '../repositories/models/get_snippet_request.dart'; +import '../repositories/models/save_snippet_request.dart'; + +class ExampleCache extends ChangeNotifier { + /// If set, then categories and default examples are enabled. + /// Otherwise examples can only be queried by paths. + final bool hasCatalog; + + final ExampleRepository _exampleRepository; + final categoryListsBySdk = >{}; + + final Map defaultExamplesBySdk = {}; + + // TODO(alexeyinkin): Extract, https://github.com/apache/beam/issues/23249 + bool isSelectorOpened = false; + + final _allExamplesCompleter = Completer(); + + Future get allExamplesFuture => _allExamplesCompleter.future; + + ExampleCache({ + required ExampleRepository exampleRepository, + required this.hasCatalog, + }) : _exampleRepository = exampleRepository; + + Future init() async { + if (hasCatalog) { + await Future.wait([ + _loadCategories(), + loadDefaultExamplesIfNot(), + ]); + } + } + + void setSdkCategories(Map> map) { + categoryListsBySdk.addAll(map); + _allExamplesCompleter.complete(); + } + + List getCategories(Sdk? sdk) { + return categoryListsBySdk[sdk] ?? []; + } + + Future getExampleOutput(String path, Sdk sdk) async { + return _exampleRepository.getExampleOutput( + GetPrecompiledObjectRequest(path: path, sdk: sdk), + ); + } + + Future getExampleSource(String path, Sdk sdk) async { + return _exampleRepository.getExampleSource( + GetPrecompiledObjectRequest(path: path, sdk: sdk), + ); + } + + Future getExample(String path, Sdk sdk) async { + return _exampleRepository.getExample( + GetPrecompiledObjectRequest(path: path, sdk: sdk), + ); + } + + Future getExampleLogs(String path, Sdk sdk) async { + return _exampleRepository.getExampleLogs( + GetPrecompiledObjectRequest(path: path, sdk: sdk), + ); + } + + Future getExampleGraph(String id, Sdk sdk) async { + return _exampleRepository.getExampleGraph( + GetPrecompiledObjectRequest(path: id, sdk: sdk), + ); + } + + Future loadSharedExample(String id) async { + final result = await _exampleRepository.getSnippet( + GetSnippetRequest(id: id), + ); + + return Example( + sdk: result.sdk, + name: result.files.first.name, + path: id, + description: '', + type: ExampleType.example, + source: result.files.first.code, + pipelineOptions: result.pipelineOptions, + ); + } + + Future getSnippetId({ + required List files, + required Sdk sdk, + required String pipelineOptions, + }) async { + final id = await _exampleRepository.saveSnippet( + SaveSnippetRequest( + files: files, + sdk: sdk, + pipelineOptions: pipelineOptions, + ), + ); + return id; + } + + Future loadExampleInfo(ExampleBase example) async { + if (example is Example) { + return example; + } + + //GRPC GetPrecompiledGraph errors hotfix + if (example.name == 'MinimalWordCount' && + (example.sdk == Sdk.go || example.sdk == Sdk.scio)) { + final exampleData = await Future.wait([ + getExampleSource(example.path, example.sdk), + getExampleOutput(example.path, example.sdk), + getExampleLogs(example.path, example.sdk), + ]); + + return Example.fromBase( + example, + source: exampleData[0], + outputs: exampleData[1], + logs: exampleData[2], + ); + } + + final exampleData = await Future.wait([ + getExampleSource(example.path, example.sdk), + getExampleOutput(example.path, example.sdk), + getExampleLogs(example.path, example.sdk), + getExampleGraph(example.path, example.sdk) + ]); + + return Example.fromBase( + example, + source: exampleData[0], + outputs: exampleData[1], + logs: exampleData[2], + graph: exampleData[3], + ); + } + + Future _loadCategories() async { + final result = await _exampleRepository.getListOfExamples( + const GetPrecompiledObjectsRequest( + sdk: null, + category: null, + ), + ); + + setSdkCategories(result); + } + + void changeSelectorVisibility() { + isSelectorOpened = !isSelectorOpened; + notifyListeners(); + } + + Future loadDefaultExamples() async { + if (defaultExamplesBySdk.isNotEmpty) { + return; + } + + try { + await Future.wait(Sdk.known.map(_loadDefaultExample)); + } catch (ex) { + if (defaultExamplesBySdk.isEmpty) { + rethrow; + } + // As long as any of the examples is loaded, continue. + print(ex); + // TODO: Log. + } + + notifyListeners(); + } + + Future _loadDefaultExample(Sdk sdk) async { + final exampleWithoutInfo = await _exampleRepository.getDefaultExample( + GetDefaultPrecompiledObjectRequest(sdk: sdk), + ); + + defaultExamplesBySdk[sdk] = await loadExampleInfo(exampleWithoutInfo); + } + + Future loadDefaultExamplesIfNot() async { + if (defaultExamplesBySdk.isNotEmpty) { + return; + } + + await loadDefaultExamples(); + } + + Future getCatalogExampleByPath(String path) async { + await allExamplesFuture; + + final allExamples = categoryListsBySdk.values + .expand((categories) => categories.map((c) => c.examples)) + .expand((examples) => examples); + + return allExamples.firstWhereOrNull( + (e) => e.path == path, + ); + } +} diff --git a/playground/frontend/playground_components/lib/src/constants/colors.dart b/playground/frontend/playground_components/lib/src/constants/colors.dart index 43aa2a56d85d..43be2453329d 100644 --- a/playground/frontend/playground_components/lib/src/constants/colors.dart +++ b/playground/frontend/playground_components/lib/src/constants/colors.dart @@ -34,18 +34,45 @@ class BeamColors { static const red = Color(0xffE54545); } +class BeamGraphColors { + static const node = BeamColors.grey3; + static const border = Color(0xFF45454E); + static const edge = BeamLightThemeColors.primary; +} + +class BeamNotificationColors { + static const error = Color(0xFFE54545); + static const info = Color(0xFF3E67F6); + static const success = Color(0xFF37AC66); + static const warning = Color(0xFFEEAB00); +} + class BeamLightThemeColors { + static const border = Color(0xFFE5E5E5); static const primaryBackground = BeamColors.white; static const secondaryBackground = Color(0xffFCFCFC); static const grey = Color(0xffE5E5E5); + static const listBackground = Color(0xFFA0A4AB); static const text = BeamColors.darkBlue; static const primary = Color(0xffE74D1A); + static const icon = Color(0xFFA0A4AB); + + static const code1 = Color(0xFFDA2833); + static const code2 = Color(0xFF5929B4); + static const codeComment = Color(0xFF4C6B60); } class BeamDarkThemeColors { + static const border = Color(0xFFA0A4AB); static const primaryBackground = Color(0xff18181B); static const secondaryBackground = BeamColors.darkGrey; static const grey = Color(0xff3F3F46); + static const listBackground = Color(0xFF606772); static const text = Color(0xffFFFFFF); static const primary = Color(0xffF26628); + static const icon = Color(0xFF606772); + + static const code1 = Color(0xFFDA2833); + static const code2 = Color(0xFF5929B4); + static const codeComment = Color(0xFF4C6B60); } diff --git a/playground/frontend/lib/modules/examples/models/example_origin.dart b/playground/frontend/playground_components/lib/src/constants/playground_components.dart similarity index 68% rename from playground/frontend/lib/modules/examples/models/example_origin.dart rename to playground/frontend/playground_components/lib/src/constants/playground_components.dart index ee4e0d789007..89909e112a81 100644 --- a/playground/frontend/lib/modules/examples/models/example_origin.dart +++ b/playground/frontend/playground_components/lib/src/constants/playground_components.dart @@ -16,26 +16,14 @@ * limitations under the License. */ -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:easy_localization_ext/easy_localization_ext.dart'; -enum ExampleOrigin { - empty, - content, - standard, - userShared, - catalogDefault, - ; +class PlaygroundComponents { + static const packageName = 'playground_components'; - static ExampleOrigin fromToken(String? token) { - if (token == null) { - return empty; - } - - final sdk = SDK.tryParseExamplePath(token); - if (sdk != null) { - return standard; - } - - return userShared; - } + // TODO(alexeyinkin): Make const when this is fixed: https://github.com/aissat/easy_localization_loader/issues/39 + static final translationLoader = YamlPackageAssetLoader( + packageName, + path: 'assets/translations', + ); } diff --git a/playground/frontend/playground_components/lib/src/constants/sizes.dart b/playground/frontend/playground_components/lib/src/constants/sizes.dart index 402f4351b3fe..916ae6f7243a 100644 --- a/playground/frontend/playground_components/lib/src/constants/sizes.dart +++ b/playground/frontend/playground_components/lib/src/constants/sizes.dart @@ -29,19 +29,29 @@ class BeamSizes { static const double size18 = 18; static const double size20 = 20; static const double size24 = 24; + static const double size30 = 30; static const double size32 = 32; static const double size36 = 36; static const double size40 = 40; + static const double size64 = 64; static const double appBarHeight = 55; + static const double buttonHeight = 40; + static const double headerButtonHeight = 46; + static const double loadingIndicator = 40; static const double splitViewSeparator = BeamSizes.size8; } class BeamBorderRadius { static const double small = BeamSizes.size4; static const double large = BeamSizes.size8; + static const double infinite = 1000; // TODO: Use StadiumBorder } class BeamIconSizes { + static const double xs = BeamSizes.size8; + static const double small = BeamSizes.size16; static const double large = BeamSizes.size32; + + static const double largeSplashRadius = 24; } diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/catalog_default_example_loader.dart similarity index 65% rename from playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/catalog_default_example_loader.dart index e07d4cb67d6e..d07d08970265 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/catalog_default_example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/catalog_default_example_loader.dart @@ -16,28 +16,28 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; +import '../../cache/example_cache.dart'; +import '../../models/example.dart'; +import '../../models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart'; +import 'example_loader.dart'; class CatalogDefaultExampleLoader extends ExampleLoader { final CatalogDefaultExampleLoadingDescriptor descriptor; - final ExampleState exampleState; + final ExampleCache exampleCache; const CatalogDefaultExampleLoader({ required this.descriptor, - required this.exampleState, + required this.exampleCache, }); @override - Future get future async { - if (!exampleState.hasExampleCatalog) { + Future get future async { + if (!exampleCache.hasCatalog) { throw Exception('Default example requires a catalog in ExampleState'); } - await exampleState.loadDefaultExamplesIfNot(); - final result = exampleState.defaultExamplesMap[descriptor.sdk]; + await exampleCache.loadDefaultExamplesIfNot(); + final result = exampleCache.defaultExamplesBySdk[descriptor.sdk]; if (result == null) { throw Exception('Default example not found for $descriptor'); diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/content_example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/content_example_loader.dart similarity index 72% rename from playground/frontend/lib/pages/playground/states/example_loaders/content_example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/content_example_loader.dart index 117bf139f525..a196d7122a7e 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/content_example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/content_example_loader.dart @@ -16,24 +16,29 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; +import '../../cache/example_cache.dart'; +import '../../models/example.dart'; +import '../../models/example_base.dart'; +import '../../models/example_loading_descriptors/content_example_loading_descriptor.dart'; +import 'example_loader.dart'; class ContentExampleLoader extends ExampleLoader { final ContentExampleLoadingDescriptor descriptor; const ContentExampleLoader({ required this.descriptor, + // TODO(alexeyinkin): Remove when this lands: https://github.com/dart-lang/language/issues/1813 + required ExampleCache exampleCache, }); @override - Future get future async => ExampleModel( + Future get future async => Example( sdk: descriptor.sdk, name: descriptor.name ?? 'Embedded_Example', path: '', description: '', type: ExampleType.example, source: descriptor.content, + pipelineOptions: '', ); } diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/empty_example_loader.dart similarity index 70% rename from playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/empty_example_loader.dart index 3f1acdc87bd2..8f3b3d07e4c5 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/empty_example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/empty_example_loader.dart @@ -16,23 +16,29 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; +import '../../cache/example_cache.dart'; +import '../../models/example.dart'; +import '../../models/example_base.dart'; +import '../../models/example_loading_descriptors/empty_example_loading_descriptor.dart'; +import 'example_loader.dart'; class EmptyExampleLoader extends ExampleLoader { final EmptyExampleLoadingDescriptor descriptor; const EmptyExampleLoader({ required this.descriptor, + // TODO(alexeyinkin): Remove when this lands: https://github.com/dart-lang/language/issues/1813 + required ExampleCache exampleCache, }); @override - Future get future async => ExampleModel( + Future get future async => Example( sdk: descriptor.sdk, name: 'Embedded_Example', path: '', description: '', type: ExampleType.example, + source: '', + pipelineOptions: '', ); } diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader.dart similarity index 89% rename from playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader.dart index 9c6b256683cc..c90f03745da7 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader.dart @@ -16,10 +16,10 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_model.dart'; +import '../../models/example.dart'; abstract class ExampleLoader { const ExampleLoader(); - Future get future; + Future get future; } diff --git a/playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader_factory.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader_factory.dart new file mode 100644 index 000000000000..9a16d7d4b990 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/example_loader_factory.dart @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../../cache/example_cache.dart'; +import '../../models/example_loading_descriptors/example_loading_descriptor.dart'; +import 'example_loader.dart'; + +typedef ExampleLoaderFactoryFunction + = ExampleLoader Function({ + required D descriptor, + required ExampleCache exampleCache, +}); + +class ExampleLoaderFactory { + final factories = {}; + + void add( + ExampleLoaderFactoryFunction function, + ) { + factories[D] = ({ + required ExampleLoadingDescriptor descriptor, + required ExampleCache exampleCache, + }) { + return function(descriptor: descriptor as D, exampleCache: exampleCache); + }; + } + + ExampleLoader? create({ + required D descriptor, + required ExampleCache exampleCache, + }) { + return factories[descriptor.runtimeType]?.call( + descriptor: descriptor, + exampleCache: exampleCache, + ); + } +} + diff --git a/playground/frontend/playground_components/lib/src/controllers/example_loaders/examples_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/examples_loader.dart new file mode 100644 index 000000000000..6d872e677512 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/examples_loader.dart @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:collection/collection.dart'; + +import '../../models/example_loading_descriptors/example_loading_descriptor.dart'; +import '../../models/example_loading_descriptors/examples_loading_descriptor.dart'; +import '../../models/sdk.dart'; +import '../playground_controller.dart'; +import 'catalog_default_example_loader.dart'; +import 'content_example_loader.dart'; +import 'empty_example_loader.dart'; +import 'example_loader_factory.dart'; +import 'standard_example_loader.dart'; +import 'user_shared_example_loader.dart'; + +class ExamplesLoader { + final defaultFactory = ExampleLoaderFactory(); + PlaygroundController? _playgroundController; + ExamplesLoadingDescriptor? _descriptor; + + ExamplesLoader() { + defaultFactory.add(CatalogDefaultExampleLoader.new); + defaultFactory.add(ContentExampleLoader.new); + defaultFactory.add(EmptyExampleLoader.new); + defaultFactory.add(StandardExampleLoader.new); + defaultFactory.add(UserSharedExampleLoader.new); + } + + void setPlaygroundController(PlaygroundController value) { + _playgroundController = value; + } + + Future load(ExamplesLoadingDescriptor descriptor) async { + if (_descriptor == descriptor) { + return; + } + + _descriptor = descriptor; + await Future.wait( + descriptor.descriptors.map( + (one) => loadOne(group: descriptor, one: one), + ), + ); + + final sdk = descriptor.initialSdk; + if (sdk != null) { + _playgroundController!.setSdk(sdk); + } + } + + Future loadDefaultIfAny(Sdk sdk) async { + final group = _descriptor; + final one = group?.lazyLoadDescriptors[sdk]?.firstOrNull; + + if (group == null || one == null) { + return; + } + + return loadOne( + group: group, + one: one, + ); + } + + Future loadOne({ + required ExamplesLoadingDescriptor group, + required ExampleLoadingDescriptor one, + }) async { + final loader = defaultFactory.create( + descriptor: one, + exampleCache: _playgroundController!.exampleCache, + ); + + if (loader == null) { + // TODO: Log. + print('Cannot create example loader for $one'); + return; + } + + final example = await loader.future; + _playgroundController!.setExample( + example, + setCurrentSdk: + example.sdk == group.initialSdk || group.initialSdk == null, + ); + } +} diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/standard_example_loader.dart similarity index 61% rename from playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/standard_example_loader.dart index 667f8f24e1dc..23c429ebab13 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/standard_example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/standard_example_loader.dart @@ -18,11 +18,12 @@ import 'dart:async'; -import 'package:playground/modules/examples/models/example_loading_descriptors/standard_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; +import '../../cache/example_cache.dart'; +import '../../models/example.dart'; +import '../../models/example_base.dart'; +import '../../models/example_loading_descriptors/standard_example_loading_descriptor.dart'; +import '../../models/sdk.dart'; +import 'example_loader.dart'; /// Loads a given example from the local cache, then adds info from network. /// @@ -30,20 +31,20 @@ import 'package:playground/pages/playground/states/examples_state.dart'; /// its cache. So it only completes if this is successful. class StandardExampleLoader extends ExampleLoader { final StandardExampleLoadingDescriptor descriptor; - final ExampleState exampleState; - final _completer = Completer(); + final ExampleCache exampleCache; + final _completer = Completer(); @override - Future get future => _completer.future; + Future get future => _completer.future; StandardExampleLoader({ required this.descriptor, - required this.exampleState, + required this.exampleCache, }) { _load(); } - void _load() async { + Future _load() async { final example = await _loadExampleWithoutInfo(); if (example == null) { @@ -52,23 +53,23 @@ class StandardExampleLoader extends ExampleLoader { } _completer.complete( - exampleState.loadExampleInfo(example), + exampleCache.loadExampleInfo(example), ); } - Future _loadExampleWithoutInfo() { - return exampleState.hasExampleCatalog - ? exampleState.getCatalogExampleByPath(descriptor.path) + Future _loadExampleWithoutInfo() { + return exampleCache.hasCatalog + ? exampleCache.getCatalogExampleByPath(descriptor.path) : _loadExampleFromRepository(); } - Future _loadExampleFromRepository() async { - final sdk = SDK.tryParseExamplePath(descriptor.path); + Future _loadExampleFromRepository() async { + final sdk = Sdk.tryParseExamplePath(descriptor.path); if (sdk == null) { return null; } - return exampleState.getExample(descriptor.path, sdk); + return exampleCache.getExample(descriptor.path, sdk); } } diff --git a/playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart b/playground/frontend/playground_components/lib/src/controllers/example_loaders/user_shared_example_loader.dart similarity index 65% rename from playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart rename to playground/frontend/playground_components/lib/src/controllers/example_loaders/user_shared_example_loader.dart index 0a658718a0ba..f256d5c56379 100644 --- a/playground/frontend/lib/pages/playground/states/example_loaders/user_shared_example_loader.dart +++ b/playground/frontend/playground_components/lib/src/controllers/example_loaders/user_shared_example_loader.dart @@ -16,21 +16,21 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/example_loaders/example_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; +import '../../cache/example_cache.dart'; +import '../../models/example.dart'; +import '../../models/example_loading_descriptors/user_shared_example_loading_descriptor.dart'; +import 'example_loader.dart'; class UserSharedExampleLoader extends ExampleLoader { final UserSharedExampleLoadingDescriptor descriptor; - final ExampleState exampleState; + final ExampleCache exampleCache; UserSharedExampleLoader({ required this.descriptor, - required this.exampleState, + required this.exampleCache, }); @override - Future get future => - exampleState.loadSharedExample(descriptor.snippetId); + Future get future => + exampleCache.loadSharedExample(descriptor.snippetId); } diff --git a/playground/frontend/lib/pages/playground/states/playground_state.dart b/playground/frontend/playground_components/lib/src/controllers/playground_controller.dart similarity index 75% rename from playground/frontend/lib/pages/playground/states/playground_state.dart rename to playground/frontend/playground_components/lib/src/controllers/playground_controller.dart index 2080f2005f5c..d68a30eede37 100644 --- a/playground/frontend/lib/pages/playground/states/playground_state.dart +++ b/playground/frontend/playground_components/lib/src/controllers/playground_controller.dart @@ -19,21 +19,24 @@ import 'dart:async'; import 'dart:math'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:playground/modules/editor/controllers/snippet_editing_controller.dart'; -import 'package:playground/modules/editor/parsers/run_options_parser.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_repository.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor_factory.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/examples/models/outputs_model.dart'; -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; +import 'package:flutter/services.dart'; + +import '../cache/example_cache.dart'; +import '../models/example.dart'; +import '../models/example_base.dart'; +import '../models/example_loading_descriptors/examples_loading_descriptor.dart'; +import '../models/intents.dart'; +import '../models/outputs.dart'; +import '../models/sdk.dart'; +import '../models/shortcut.dart'; +import '../repositories/code_repository.dart'; +import '../repositories/models/run_code_request.dart'; +import '../repositories/models/run_code_result.dart'; +import '../repositories/models/shared_file.dart'; +import '../util/pipeline_options.dart'; +import 'example_loaders/examples_loader.dart'; +import 'snippet_editing_controller.dart'; const kTitleLength = 15; const kExecutionTimeUpdate = 100; @@ -45,40 +48,33 @@ const kPipelineOptionsParseError = const kCachedResultsLog = 'The results of this example are taken from the Apache Beam Playground cache.\n'; -class PlaygroundState with ChangeNotifier { - final ExampleState exampleState; +class PlaygroundController with ChangeNotifier { + final ExampleCache exampleCache; final ExamplesLoader examplesLoader; - final ExamplesLoadingDescriptor examplesLoadingDescriptor; - final _snippetEditingControllers = {}; + final _snippetEditingControllers = {}; + + Sdk? _sdk; + final CodeRepository? _codeRepository; - SDK? _sdk; - CodeRepository? _codeRepository; RunCodeResult? _result; StreamSubscription? _runSubscription; StreamController? _executionTime; - OutputType? selectedOutputFilterType; + + // TODO(alexeyinkin): Extract along with run status, https://github.com/apache/beam/issues/23248 + OutputType selectedOutputFilterType = OutputType.all; String outputResult = ''; - PlaygroundState({ - required this.exampleState, + PlaygroundController({ + required this.exampleCache, required this.examplesLoader, CodeRepository? codeRepository, - }) : examplesLoadingDescriptor = - ExamplesLoadingDescriptorFactory.fromUriParts( - path: Uri.base.path, - params: Uri.base.queryParameters, - ) { - examplesLoader.setPlaygroundState(this); - examplesLoader.load(examplesLoadingDescriptor); - - _codeRepository = codeRepository; - selectedOutputFilterType = OutputType.all; - outputResult = ''; + }) : _codeRepository = codeRepository { + examplesLoader.setPlaygroundController(this); } SnippetEditingController _getOrCreateSnippetEditingController( - SDK sdk, { + Sdk sdk, { required bool loadDefaultIfNot, }) { final existing = _snippetEditingControllers[sdk]; @@ -90,30 +86,21 @@ class PlaygroundState with ChangeNotifier { _snippetEditingControllers[sdk] = result; if (loadDefaultIfNot) { - final descriptor = - examplesLoadingDescriptor.lazyLoadDescriptors[sdk]?.firstOrNull; - - if (descriptor != null) { - examplesLoader.loadOne( - group: examplesLoadingDescriptor, - one: descriptor, - ); - } + examplesLoader.loadDefaultIfAny(sdk); } return result; } - // TODO: Return full, then shorten. + // TODO(alexeyinkin): Return full, then shorten, https://github.com/apache/beam/issues/23250 String get examplesTitle { final name = snippetEditingController?.selectedExample?.name ?? kTitle; return name.substring(0, min(kTitleLength, name.length)); } - ExampleModel? get selectedExample => - snippetEditingController?.selectedExample; + Example? get selectedExample => snippetEditingController?.selectedExample; - SDK? get sdk => _sdk; + Sdk? get sdk => _sdk; SnippetEditingController? get snippetEditingController => _snippetEditingControllers[_sdk]; @@ -142,12 +129,13 @@ class PlaygroundState with ChangeNotifier { return snippetEditingController?.isChanged ?? false; } + // TODO(alexeyinkin): Single source of truth for whether graph is supported, https://github.com/apache/beam/issues/23251 bool get graphAvailable => selectedExample?.type != ExampleType.test && - [SDK.java, SDK.python].contains(sdk); + [Sdk.java, Sdk.python].contains(sdk); void setExample( - ExampleModel example, { + Example example, { required bool setCurrentSdk, }) { if (setCurrentSdk) { @@ -173,7 +161,7 @@ class PlaygroundState with ChangeNotifier { } void setSdk( - SDK sdk, { + Sdk sdk, { bool notify = true, }) { _sdk = sdk; @@ -233,7 +221,7 @@ class PlaygroundState with ChangeNotifier { final parsedPipelineOptions = parsePipelineOptions(controller.pipelineOptions); if (parsedPipelineOptions == null) { - _result = RunCodeResult( + _result = const RunCodeResult( status: RunCodeStatus.compileError, errorMessage: kPipelineOptionsParseError, ); @@ -245,7 +233,7 @@ class PlaygroundState with ChangeNotifier { if (!isExampleChanged && controller.selectedExample?.outputs != null) { _showPrecompiledResult(controller); } else { - final request = RunCodeRequestWrapper( + final request = RunCodeRequest( code: controller.codeController.text, sdk: controller.sdk, pipelineOptions: parsedPipelineOptions, @@ -265,28 +253,31 @@ class PlaygroundState with ChangeNotifier { } Future cancelRun() async { - _runSubscription?.cancel(); + await _runSubscription?.cancel(); final pipelineUuid = result?.pipelineUuid ?? ''; + if (pipelineUuid.isNotEmpty) { await _codeRepository?.cancelExecution(pipelineUuid); } + _result = RunCodeResult( status: RunCodeStatus.finished, output: _result?.output, log: (_result?.log ?? '') + kExecutionCancelledText, graph: _result?.graph, ); - String log = _result?.log ?? ''; - String output = _result?.output ?? ''; + + final log = _result?.log ?? ''; + final output = _result?.output ?? ''; setOutputResult(log + output); - _executionTime?.close(); + await _executionTime?.close(); notifyListeners(); } Future _showPrecompiledResult( SnippetEditingController snippetEditingController, ) async { - _result = RunCodeResult( + _result = const RunCodeResult( status: RunCodeStatus.preparation, ); final selectedExample = snippetEditingController.selectedExample!; @@ -303,8 +294,8 @@ class PlaygroundState with ChangeNotifier { graph: selectedExample.graph, ); - filterOutput(selectedOutputFilterType ?? OutputType.all); - _executionTime?.close(); + filterOutput(selectedOutputFilterType); + await _executionTime?.close(); notifyListeners(); } @@ -359,7 +350,7 @@ class PlaygroundState with ChangeNotifier { Future getSnippetId() { final controller = requireSnippetEditingController(); - return exampleState.getSnippetId( + return exampleCache.getSnippetId( files: [SharedFile(code: controller.codeController.text, isMain: true)], sdk: controller.sdk, pipelineOptions: controller.pipelineOptions, @@ -377,4 +368,32 @@ class PlaygroundState with ChangeNotifier { .toList(growable: false), ); } + + late BeamShortcut runShortcut = BeamShortcut( + shortcuts: LogicalKeySet( + LogicalKeyboardKey.meta, + LogicalKeyboardKey.enter, + ), + actionIntent: const RunIntent(), + createAction: (BuildContext context) => CallbackAction( + onInvoke: (_) => runCode(), + ), + ); + + late BeamShortcut resetShortcut = BeamShortcut( + shortcuts: LogicalKeySet( + LogicalKeyboardKey.meta, + LogicalKeyboardKey.shift, + LogicalKeyboardKey.keyE, + ), + actionIntent: const ResetIntent(), + createAction: (BuildContext context) => CallbackAction( + onInvoke: (_) => reset(), + ), + ); + + List get shortcuts => [ + runShortcut, + resetShortcut, + ]; } diff --git a/playground/frontend/lib/modules/editor/controllers/snippet_editing_controller.dart b/playground/frontend/playground_components/lib/src/controllers/snippet_editing_controller.dart similarity index 80% rename from playground/frontend/lib/modules/editor/controllers/snippet_editing_controller.dart rename to playground/frontend/playground_components/lib/src/controllers/snippet_editing_controller.dart index 92acbfc6d434..e687a16d451d 100644 --- a/playground/frontend/lib/modules/editor/controllers/snippet_editing_controller.dart +++ b/playground/frontend/playground_components/lib/src/controllers/snippet_editing_controller.dart @@ -18,20 +18,21 @@ import 'package:code_text_field/code_text_field.dart'; import 'package:flutter/widgets.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; + +import '../models/example.dart'; +import '../models/example_loading_descriptors/content_example_loading_descriptor.dart'; +import '../models/example_loading_descriptors/example_loading_descriptor.dart'; +import '../models/sdk.dart'; class SnippetEditingController extends ChangeNotifier { - final SDK sdk; + final Sdk sdk; final CodeController codeController; - ExampleModel? _selectedExample; + Example? _selectedExample; String _pipelineOptions; SnippetEditingController({ required this.sdk, - ExampleModel? selectedExample, + Example? selectedExample, String pipelineOptions = '', }) : codeController = CodeController( language: sdk.highlightMode, @@ -40,14 +41,14 @@ class SnippetEditingController extends ChangeNotifier { _selectedExample = selectedExample, _pipelineOptions = pipelineOptions; - set selectedExample(ExampleModel? value) { + set selectedExample(Example? value) { _selectedExample = value; codeController.text = _selectedExample?.source ?? ''; _pipelineOptions = _selectedExample?.pipelineOptions ?? ''; notifyListeners(); } - ExampleModel? get selectedExample => _selectedExample; + Example? get selectedExample => _selectedExample; set pipelineOptions(String value) { _pipelineOptions = value; @@ -77,7 +78,8 @@ class SnippetEditingController extends ChangeNotifier { /// current content. ExampleLoadingDescriptor getLoadingDescriptor() { // TODO: Return other classes for unchanged standard examples, - // user-shared examples, and an empty editor. + // user-shared examples, and an empty editor, + // https://github.com/apache/beam/issues/23252 return ContentExampleLoadingDescriptor( content: codeController.text, name: _selectedExample?.name, diff --git a/playground/frontend/lib/modules/examples/models/category_model.dart b/playground/frontend/playground_components/lib/src/models/category_with_examples.dart similarity index 56% rename from playground/frontend/lib/modules/examples/models/category_model.dart rename to playground/frontend/playground_components/lib/src/models/category_with_examples.dart index 20a224bb3a56..e9b1ba392537 100644 --- a/playground/frontend/lib/modules/examples/models/category_model.dart +++ b/playground/frontend/playground_components/lib/src/models/category_with_examples.dart @@ -16,23 +16,31 @@ * limitations under the License. */ -import 'package:playground/constants/params.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; +import 'example_base.dart'; -class CategoryModel with Comparable { - final String name; - final List examples; +const _pinnedTitle = 'quick start'; - const CategoryModel({required this.name, required this.examples}); +class CategoryWithExamples with Comparable { + // TODO(alexeyinkin): Sort on the backend instead, then make const constructor, https://github.com/apache/beam/issues/23083 + final bool isPinned; + final String title; + final List examples; + + CategoryWithExamples({ + required this.title, + required this.examples, + }) : isPinned = title.toLowerCase() == _pinnedTitle; @override - int compareTo(CategoryModel other) { - if (name.toLowerCase() == kQuickStartCategoryName) { + int compareTo(CategoryWithExamples other) { + if (isPinned && !other.isPinned) { return -1; } - if (other.name.toLowerCase() == kQuickStartCategoryName) { + + if (!isPinned && other.isPinned) { return 1; } - return name.toLowerCase().compareTo(other.name.toLowerCase()); + + return title.toLowerCase().compareTo(other.title.toLowerCase()); } } diff --git a/playground/frontend/playground_components/lib/src/models/example.dart b/playground/frontend/playground_components/lib/src/models/example.dart new file mode 100644 index 000000000000..e35856a6264f --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/example.dart @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'example_base.dart'; + +class Example extends ExampleBase { + final String source; + final String? outputs; + final String? logs; + final String? graph; + + const Example({ + required super.sdk, + required super.type, + required super.name, + required super.path, + required super.description, + super.contextLine, + super.isMultiFile, + super.link, + required super.pipelineOptions, + required this.source, + this.outputs, + this.logs, + this.graph, + }); + + Example.fromBase( + ExampleBase example, { + required this.source, + required this.outputs, + required this.logs, + this.graph, + }) : super( + sdk: example.sdk, + name: example.name, + path: example.path, + description: example.description, + type: example.type, + contextLine: example.contextLine, + isMultiFile: example.isMultiFile, + link: example.link, + pipelineOptions: example.pipelineOptions, + ); +} diff --git a/playground/frontend/lib/modules/examples/models/example_model.dart b/playground/frontend/playground_components/lib/src/models/example_base.dart similarity index 60% rename from playground/frontend/lib/modules/examples/models/example_model.dart rename to playground/frontend/playground_components/lib/src/models/example_base.dart index 054dddb500a4..2a340e56ba2b 100644 --- a/playground/frontend/lib/modules/examples/models/example_model.dart +++ b/playground/frontend/playground_components/lib/src/models/example_base.dart @@ -16,7 +16,9 @@ * limitations under the License. */ -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:equatable/equatable.dart'; + +import 'sdk.dart'; enum ExampleType { all, @@ -40,22 +42,18 @@ extension ExampleTypeToString on ExampleType { } } -class ExampleModel with Comparable { - final SDK sdk; +class ExampleBase with Comparable, EquatableMixin { + final Sdk sdk; final ExampleType type; final String name; final String path; final String description; - int contextLine; - bool isMultiFile; - String? link; - String? source; - String? outputs; - String? logs; - String? pipelineOptions; - String? graph; + final int contextLine; + final bool isMultiFile; + final String? link; + final String pipelineOptions; - ExampleModel({ + const ExampleBase({ required this.sdk, required this.name, required this.path, @@ -64,47 +62,14 @@ class ExampleModel with Comparable { this.contextLine = 1, this.isMultiFile = false, this.link, - this.source, - this.outputs, - this.logs, - this.pipelineOptions, - this.graph, + required this.pipelineOptions, }); - setSource(String source) { - this.source = source; - } - - setOutputs(String outputs) { - this.outputs = outputs; - } - - setLogs(String logs) { - this.logs = logs; - } - - setGraph(String graph) { - this.graph = graph; - } - - setContextLine(int contextLine) { - this.contextLine = contextLine; - } - - bool isInfoFetched() { - // checking only source, because outputs/logs can be empty - return source?.isNotEmpty ?? false; - } - - @override - bool operator ==(Object other) => - identical(this, other) || (other is ExampleModel && path == other.path); - @override - int get hashCode => path.hashCode; + List get props => [path]; @override - int compareTo(ExampleModel other) { + int compareTo(ExampleBase other) { return name.toLowerCase().compareTo(other.name.toLowerCase()); } } diff --git a/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart new file mode 100644 index 000000000000..085ee1d9dc0c --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/catalog_default_example_loading_descriptor.dart @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../sdk.dart'; +import 'example_loading_descriptor.dart'; + +class CatalogDefaultExampleLoadingDescriptor extends ExampleLoadingDescriptor { + final Sdk sdk; + + const CatalogDefaultExampleLoadingDescriptor({ + required this.sdk, + }); + + @override + List get props => [sdk.id]; +} diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/content_example_loading_descriptor.dart similarity index 70% rename from playground/frontend/lib/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart rename to playground/frontend/playground_components/lib/src/models/example_loading_descriptors/content_example_loading_descriptor.dart index f981f8a9d018..13be92a506d0 100644 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/content_example_loading_descriptor.dart @@ -16,9 +16,8 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_origin.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import '../sdk.dart'; +import 'example_loading_descriptor.dart'; class ContentExampleLoadingDescriptor extends ExampleLoadingDescriptor { /// The source code. @@ -27,7 +26,7 @@ class ContentExampleLoadingDescriptor extends ExampleLoadingDescriptor { /// The name of the example, if any, to show in the dropdown. final String? name; - final SDK sdk; + final Sdk sdk; const ContentExampleLoadingDescriptor({ required this.content, @@ -61,35 +60,17 @@ class ContentExampleLoadingDescriptor extends ExampleLoadingDescriptor { return map['name']?.toString(); } - static SDK? _tryParseSdk(Map map) { - return SDK.tryParse(map['sdk']); + static Sdk? _tryParseSdk(Map map) { + return Sdk.tryParse(map['sdk']); } @override - int get hashCode => Object.hash( - content.hashCode, - sdk.hashCode, - ); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - - return other is ContentExampleLoadingDescriptor && - content == other.content && - name == other.name && - sdk == other.sdk; - } - - @override - ExampleOrigin get origin => ExampleOrigin.content; + List get props => [content, sdk.id]; @override Map toJson() => { 'content': content, 'name': name, - 'sdk': sdk.name, + 'sdk': sdk.id, }; } diff --git a/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/empty_example_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/empty_example_loading_descriptor.dart new file mode 100644 index 000000000000..74721712b66a --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/empty_example_loading_descriptor.dart @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../sdk.dart'; +import 'example_loading_descriptor.dart'; + +class EmptyExampleLoadingDescriptor extends ExampleLoadingDescriptor { + final Sdk sdk; + + const EmptyExampleLoadingDescriptor({ + required this.sdk, + }); + + @override + List get props => [sdk]; +} diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/example_loading_descriptor.dart similarity index 79% rename from playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart rename to playground/frontend/playground_components/lib/src/models/example_loading_descriptors/example_loading_descriptor.dart index 4867544e5056..35c3cf18100a 100644 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/example_loading_descriptor.dart @@ -16,15 +16,10 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_origin.dart'; +import 'package:equatable/equatable.dart'; -abstract class ExampleLoadingDescriptor { +abstract class ExampleLoadingDescriptor with EquatableMixin { const ExampleLoadingDescriptor(); - ExampleOrigin get origin; - - @override - String toString() => '$origin'; - - Map toJson(); + Map toJson() => throw UnimplementedError(); } diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/examples_loading_descriptor.dart similarity index 88% rename from playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart rename to playground/frontend/playground_components/lib/src/models/example_loading_descriptors/examples_loading_descriptor.dart index 0fce67a0d243..6a221376c3b8 100644 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/examples_loading_descriptor.dart @@ -17,21 +17,24 @@ */ import 'package:collection/collection.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:meta/meta.dart'; +import '../sdk.dart'; +import 'example_loading_descriptor.dart'; + +@immutable class ExamplesLoadingDescriptor { /// The descriptors to be loaded right away. final List descriptors; /// The descriptors to be loaded when an SDK is selected /// that has nothing loaded yet. - final Map> lazyLoadDescriptors; + final Map> lazyLoadDescriptors; /// If set, sets the SDK to this and does not change it when loading /// new examples. Otherwise sets the SDK to that of each loaded example /// of [descriptors]. - final SDK? initialSdk; + final Sdk? initialSdk; const ExamplesLoadingDescriptor({ required this.descriptors, @@ -46,7 +49,7 @@ class ExamplesLoadingDescriptor { buffer.write(descriptors.map((e) => e.toString()).join('_')); for (final descriptor in lazyLoadDescriptors.entries) { - buffer.write(', Lazy Load ${descriptor.key.name}: '); + buffer.write(', Lazy Load ${descriptor.key.id}: '); buffer.write(descriptor.value.map((e) => e.toString()).join('_')); } diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_response.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/standard_example_loading_descriptor.dart similarity index 75% rename from playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_response.dart rename to playground/frontend/playground_components/lib/src/models/example_loading_descriptors/standard_example_loading_descriptor.dart index 609c61df3ad0..8ac57c624a92 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/get_list_of_examples_response.dart +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/standard_example_loading_descriptor.dart @@ -16,11 +16,15 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'example_loading_descriptor.dart'; -class GetListOfExampleResponse { - final Map> categories; +class StandardExampleLoadingDescriptor extends ExampleLoadingDescriptor { + final String path; - GetListOfExampleResponse(this.categories); + const StandardExampleLoadingDescriptor({ + required this.path, + }); + + @override + List get props => [path]; } diff --git a/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart new file mode 100644 index 000000000000..1bcbe0dc60af --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/example_loading_descriptors/user_shared_example_loading_descriptor.dart @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'example_loading_descriptor.dart'; + +class UserSharedExampleLoadingDescriptor extends ExampleLoadingDescriptor { + final String snippetId; + + const UserSharedExampleLoadingDescriptor({ + required this.snippetId, + }); + + @override + List get props => [snippetId]; +} diff --git a/playground/frontend/playground_components/lib/src/models/intents.dart b/playground/frontend/playground_components/lib/src/models/intents.dart new file mode 100644 index 000000000000..74ef7c4c557e --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/intents.dart @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/widgets.dart'; + +class BeamIntent extends Intent { + final String slug; + + const BeamIntent({ + required this.slug, + }); +} + +class ResetIntent extends BeamIntent { + const ResetIntent() : super(slug: 'intents.playground.reset'); +} + +class RunIntent extends BeamIntent { + const RunIntent() : super(slug: 'intents.playground.run'); +} diff --git a/playground/frontend/lib/modules/examples/models/outputs_model.dart b/playground/frontend/playground_components/lib/src/models/outputs.dart similarity index 89% rename from playground/frontend/lib/modules/examples/models/outputs_model.dart rename to playground/frontend/playground_components/lib/src/models/outputs.dart index 19f2feaa50bd..fa1a360063c0 100644 --- a/playground/frontend/lib/modules/examples/models/outputs_model.dart +++ b/playground/frontend/playground_components/lib/src/models/outputs.dart @@ -23,10 +23,14 @@ enum OutputType { graph, } -class OutputsModel { +class Outputs { final String output; final String graph; final String log; - OutputsModel(this.output, this.graph, this.log); + const Outputs({ + required this.output, + required this.graph, + required this.log, + }); } diff --git a/playground/frontend/playground_components/lib/src/models/sdk.dart b/playground/frontend/playground_components/lib/src/models/sdk.dart new file mode 100644 index 000000000000..ae29dc1a06f3 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/models/sdk.dart @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:collection/collection.dart'; +import 'package:equatable/equatable.dart'; +import 'package:highlight/highlight_core.dart'; +import 'package:highlight/languages/go.dart' as mode_go; +import 'package:highlight/languages/java.dart' as mode_java; +import 'package:highlight/languages/python.dart' as mode_python; +import 'package:highlight/languages/scala.dart' as mode_scala; + +class Sdk with EquatableMixin { + final String id; + final String title; + + const Sdk({ + required this.id, + required this.title, + }); + + static const java = Sdk(id: 'java', title: 'Java'); + static const go = Sdk(id: 'go', title: 'Go'); + static const python = Sdk(id: 'python', title: 'Python'); + static const scio = Sdk(id: 'scio', title: 'SCIO'); + + static const known = [ + java, + go, + python, + scio, + ]; + + @override + List get props => [ + id, + title, + ]; + + /// A temporary solution while we wait for the backend to add + /// sdk in example responses. + static Sdk? tryParseExamplePath(String? path) { + if (path == null) { + return null; + } + + if (path.startsWith('SDK_JAVA')) { + return java; + } + + if (path.startsWith('SDK_GO')) { + return go; + } + + if (path.startsWith('SDK_PYTHON')) { + return python; + } + + if (path.startsWith('SDK_SCIO')) { + return scio; + } + + return null; + } + + static Sdk? tryParse(Object? value) { + if (value is! String) { + return null; + } + + return known.firstWhereOrNull((e) => e.id == value); + } + + static final _idToHighlightMode = { + Sdk.java.id: mode_java.java, + Sdk.go.id: mode_go.go, + Sdk.python: mode_python.python, + Sdk.scio: mode_scala.scala, + }; + + Mode? get highlightMode => _idToHighlightMode[id]; +} diff --git a/playground/frontend/lib/components/horizontal_divider/horizontal_divider.dart b/playground/frontend/playground_components/lib/src/models/shortcut.dart similarity index 56% rename from playground/frontend/lib/components/horizontal_divider/horizontal_divider.dart rename to playground/frontend/playground_components/lib/src/models/shortcut.dart index f935eade1025..ff4ede80f91d 100644 --- a/playground/frontend/lib/components/horizontal_divider/horizontal_divider.dart +++ b/playground/frontend/playground_components/lib/src/models/shortcut.dart @@ -17,25 +17,34 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/sizes.dart'; +import 'package:flutter/services.dart'; -/// Replaces the Flutter's Divider which is buggy with HTML renderer, -/// see https://github.com/flutter/flutter/issues/46339 -class HorizontalDivider extends StatelessWidget { - final double? indent; +import 'intents.dart'; - const HorizontalDivider({ - super.key, - this.indent, +class BeamShortcut { + final LogicalKeySet shortcuts; + final BeamIntent actionIntent; + final CallbackAction Function(BuildContext) createAction; + + BeamShortcut({ + required this.shortcuts, + required this.actionIntent, + required this.createAction, }); - @override - Widget build(BuildContext context) { - return Container( - height: kDividerHeight, - margin: EdgeInsets.fromLTRB(indent ?? 0, 0, indent ?? 0, 0), - color: ThemeColors.of(context).divider, - ); + static const _metaKeyName = 'CMD/CTRL'; + static const _glue = ' + '; + + String get title { + return shortcuts.keys + .map(getKeyDisplayName) + .join(_glue); + } + + String getKeyDisplayName(LogicalKeyboardKey e) { + if (e.keyId == LogicalKeyboardKey.meta.keyId) { + return _metaKeyName; + } + return e.keyLabel; } } diff --git a/playground/frontend/lib/modules/notifications/components/base_notification.dart b/playground/frontend/playground_components/lib/src/notifications/base_notification.dart similarity index 82% rename from playground/frontend/lib/modules/notifications/components/base_notification.dart rename to playground/frontend/playground_components/lib/src/notifications/base_notification.dart index e115f8dc578d..fe23950959ca 100644 --- a/playground/frontend/lib/modules/notifications/components/base_notification.dart +++ b/playground/frontend/playground_components/lib/src/notifications/base_notification.dart @@ -18,8 +18,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/constants/font_weight.dart'; -import 'package:playground/constants/sizes.dart'; + +import '../constants/sizes.dart'; const kNotificationBorderWidth = 4.0; const kMaxTextWidth = 300.0; @@ -31,12 +31,12 @@ class BaseNotification extends StatelessWidget { final String asset; const BaseNotification({ - Key? key, + super.key, required this.title, required this.notification, required this.color, required this.asset, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -58,8 +58,8 @@ class BaseNotification extends StatelessWidget { decoration: BoxDecoration( color: color, borderRadius: const BorderRadius.only( - topLeft: Radius.circular(kLgBorderRadius), - bottomLeft: Radius.circular(kLgBorderRadius), + topLeft: Radius.circular(BeamSizes.size8), + bottomLeft: Radius.circular(BeamSizes.size8), ), ), ), @@ -70,19 +70,18 @@ class BaseNotification extends StatelessWidget { final textTheme = Theme.of(context).textTheme.bodyText1; return Positioned( child: Padding( - padding: const EdgeInsets.all(kLgSpacing), + padding: const EdgeInsets.all(BeamSizes.size12), child: Row( - crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.asset(asset), - const SizedBox(width: kLgSpacing), + const SizedBox(width: BeamSizes.size12), Wrap( direction: Axis.vertical, - spacing: kSmSpacing, + spacing: BeamSizes.size4, children: [ Text( title, - style: textTheme?.copyWith(fontWeight: kBoldWeight), + style: textTheme?.copyWith(fontWeight: FontWeight.w600), ), SizedBox( width: kMaxTextWidth, diff --git a/playground/frontend/lib/modules/notifications/components/notification.dart b/playground/frontend/playground_components/lib/src/notifications/notification.dart similarity index 82% rename from playground/frontend/lib/modules/notifications/components/notification.dart rename to playground/frontend/playground_components/lib/src/notifications/notification.dart index 9fcb25e8af3b..264361174c56 100644 --- a/playground/frontend/lib/modules/notifications/components/notification.dart +++ b/playground/frontend/playground_components/lib/src/notifications/notification.dart @@ -18,9 +18,10 @@ import 'package:aligned_dialog/aligned_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:playground/constants/assets.dart'; -import 'package:playground/constants/colors.dart'; -import 'package:playground/modules/notifications/components/base_notification.dart'; + +import '../constants/colors.dart'; +import '../generated/assets.gen.dart'; +import 'base_notification.dart'; const kDialogOffset = Offset(0, 30); @@ -35,8 +36,8 @@ class NotificationManager { BaseNotification( title: title, notification: notification, - color: kErrorNotificationColor, - asset: kErrorNotificationIconAsset, + color: BeamNotificationColors.error, + asset: Assets.notificationIcons.error, ), ); } @@ -51,8 +52,8 @@ class NotificationManager { BaseNotification( title: title, notification: notification, - color: kInfoNotificationColor, - asset: kInfoNotificationIconAsset, + color: BeamNotificationColors.info, + asset: Assets.notificationIcons.info, ), ); } @@ -67,8 +68,8 @@ class NotificationManager { BaseNotification( title: title, notification: notification, - color: kWarningNotificationColor, - asset: kWarningNotificationIconAsset, + color: BeamNotificationColors.warning, + asset: Assets.notificationIcons.warning, ), ); } @@ -83,8 +84,8 @@ class NotificationManager { BaseNotification( title: title, notification: notification, - color: kSuccessNotificationColor, - asset: kSuccessNotificationIconAsset, + color: BeamNotificationColors.success, + asset: Assets.notificationIcons.success, ), ); } diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/code_client.dart b/playground/frontend/playground_components/lib/src/repositories/code_client/code_client.dart similarity index 66% rename from playground/frontend/lib/modules/editor/repository/code_repository/code_client/code_client.dart rename to playground/frontend/playground_components/lib/src/repositories/code_client/code_client.dart index 849429c120af..2024e0aa82b8 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/code_client.dart +++ b/playground/frontend/playground_components/lib/src/repositories/code_client/code_client.dart @@ -16,53 +16,45 @@ * limitations under the License. */ -import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/run_code_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'; +import '../models/check_status_response.dart'; +import '../models/output_response.dart'; +import '../models/run_code_request.dart'; +import '../models/run_code_response.dart'; abstract class CodeClient { - Future runCode(RunCodeRequestWrapper request); + Future runCode(RunCodeRequest request); Future cancelExecution(String pipelineUuid); Future checkStatus( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getCompileOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getRunOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getLogOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getRunErrorOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getValidationErrorOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getPreparationErrorOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); Future getGraphOutput( String pipelineUuid, - RunCodeRequestWrapper request, ); } diff --git a/playground/frontend/playground_components/lib/src/repositories/code_client/grpc_code_client.dart b/playground/frontend/playground_components/lib/src/repositories/code_client/grpc_code_client.dart new file mode 100644 index 000000000000..729ce4cd19f9 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/code_client/grpc_code_client.dart @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:grpc/grpc.dart'; + +import '../../api/iis_workaround_channel.dart'; +import '../../api/v1/api.pbgrpc.dart' as grpc; +import '../../models/sdk.dart'; +import '../../util/pipeline_options.dart'; +import '../../util/replace_incorrect_symbols.dart'; +import '../models/check_status_response.dart'; +import '../models/output_response.dart'; +import '../models/run_code_error.dart'; +import '../models/run_code_request.dart'; +import '../models/run_code_response.dart'; +import '../models/run_code_result.dart'; +import '../sdk_grpc_extension.dart'; +import 'code_client.dart'; + +const kGeneralError = 'Failed to execute code'; + +class GrpcCodeClient implements CodeClient { + final grpc.PlaygroundServiceClient _defaultClient; + final Map _runnerUrlsById; + + factory GrpcCodeClient({ + required String url, + required Map runnerUrlsById, + }) { + final channel = IisWorkaroundChannel.xhr( + Uri.parse(url), + ); + + return GrpcCodeClient._( + client: grpc.PlaygroundServiceClient(channel), + runnerUrlsById: runnerUrlsById, + ); + } + + GrpcCodeClient._({ + required grpc.PlaygroundServiceClient client, + required Map runnerUrlsById, + }) : _defaultClient = client, + _runnerUrlsById = runnerUrlsById; + + @override + Future runCode(RunCodeRequest request) async { + final client = _createRunCodeClient(request.sdk); + final response = await _runSafely( + () => client.runCode(_grpcRunCodeRequest(request)), + ); + + return RunCodeResponse( + pipelineUuid: response.pipelineUuid, + ); + } + + @override + Future cancelExecution(String pipelineUuid) { + return _runSafely(() => + _defaultClient.cancel(grpc.CancelRequest(pipelineUuid: pipelineUuid))); + } + + @override + Future checkStatus( + String pipelineUuid, + ) async { + final response = await _runSafely( + () => _defaultClient.checkStatus( + grpc.CheckStatusRequest(pipelineUuid: pipelineUuid), + ), + ); + + return CheckStatusResponse( + status: _toClientStatus(response.status), + ); + } + + @override + Future getCompileOutput( + String pipelineUuid, + ) async { + final response = await _runSafely( + () => _defaultClient.getCompileOutput( + grpc.GetCompileOutputRequest(pipelineUuid: pipelineUuid), + ), + ); + + return _toOutputResponse(response.output); + } + + @override + Future getRunOutput( + String pipelineUuid, + ) async { + try { + final response = await _runSafely( + () => _defaultClient.getRunOutput( + grpc.GetRunOutputRequest(pipelineUuid: pipelineUuid), + ), + ); + + return _toOutputResponse(response.output); + } catch (ex) { + print(ex); + return _toOutputResponse(''); + } + } + + @override + Future getLogOutput( + String pipelineUuid, + ) async { + try { + final response = await _defaultClient.getLogs( + grpc.GetLogsRequest(pipelineUuid: pipelineUuid), + ); + + return _toOutputResponse(response.output); + } catch (ex) { + print(ex); + return _toOutputResponse(''); + } + } + + @override + Future getRunErrorOutput( + String pipelineUuid, + ) async { + final response = await _defaultClient.getRunError( + grpc.GetRunErrorRequest(pipelineUuid: pipelineUuid), + ); + + return _toOutputResponse(response.output); + } + + @override + Future getValidationErrorOutput( + String pipelineUuid, + ) async { + final response = await _defaultClient.getValidationOutput( + grpc.GetValidationOutputRequest(pipelineUuid: pipelineUuid), + ); + + return _toOutputResponse(response.output); + } + + @override + Future getPreparationErrorOutput( + String pipelineUuid, + ) async { + final response = await _defaultClient.getPreparationOutput( + grpc.GetPreparationOutputRequest(pipelineUuid: pipelineUuid), + ); + + return _toOutputResponse(response.output); + } + + @override + Future getGraphOutput( + String pipelineUuid, + ) async { + try { + final response = await _defaultClient.getGraph( + grpc.GetGraphRequest(pipelineUuid: pipelineUuid), + ); + + return OutputResponse(output: response.graph); + } catch (ex) { + print(ex); + return _toOutputResponse(''); + } + } + + Future _runSafely(Future Function() invoke) async { + try { + return await invoke(); + } on GrpcError catch (error) { + throw RunCodeError(message: error.message); + } on Exception catch (_) { + throw const RunCodeError(); + } + } + + /// Run Code request should use different urls for each sdk + /// instead of the default one, because we need to code + /// sdk services for it + grpc.PlaygroundServiceClient _createRunCodeClient(Sdk sdk) { + final apiClientURL = _runnerUrlsById[sdk.id]; + + if (apiClientURL == null) { + throw Exception('Runner not found for ${sdk.id}'); + } + + final channel = IisWorkaroundChannel.xhr( + Uri.parse(apiClientURL), + ); + return grpc.PlaygroundServiceClient(channel); + } + + grpc.RunCodeRequest _grpcRunCodeRequest(RunCodeRequest request) { + return grpc.RunCodeRequest() + ..code = request.code + ..sdk = request.sdk.grpc + ..pipelineOptions = pipelineOptionsToString(request.pipelineOptions); + } + + RunCodeStatus _toClientStatus(grpc.Status status) { + switch (status) { + case grpc.Status.STATUS_UNSPECIFIED: + return RunCodeStatus.unspecified; + case grpc.Status.STATUS_VALIDATING: + case grpc.Status.STATUS_PREPARING: + return RunCodeStatus.preparation; + case grpc.Status.STATUS_COMPILING: + return RunCodeStatus.compiling; + case grpc.Status.STATUS_EXECUTING: + return RunCodeStatus.executing; + case grpc.Status.STATUS_CANCELED: + case grpc.Status.STATUS_FINISHED: + return RunCodeStatus.finished; + case grpc.Status.STATUS_COMPILE_ERROR: + return RunCodeStatus.compileError; + case grpc.Status.STATUS_RUN_TIMEOUT: + return RunCodeStatus.timeout; + case grpc.Status.STATUS_RUN_ERROR: + return RunCodeStatus.runError; + case grpc.Status.STATUS_VALIDATION_ERROR: + return RunCodeStatus.validationError; + case grpc.Status.STATUS_PREPARATION_ERROR: + return RunCodeStatus.preparationError; + case grpc.Status.STATUS_ERROR: + return RunCodeStatus.unknownError; + } + return RunCodeStatus.unspecified; + } + + OutputResponse _toOutputResponse(String response) { + return OutputResponse(output: replaceIncorrectSymbols(response)); + } +} diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart b/playground/frontend/playground_components/lib/src/repositories/code_repository.dart similarity index 76% rename from playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart rename to playground/frontend/playground_components/lib/src/repositories/code_repository.dart index 37ad6ee0d1fe..c9efc3146c09 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart +++ b/playground/frontend/playground_components/lib/src/repositories/code_repository.dart @@ -16,12 +16,12 @@ * limitations under the License. */ -import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_error.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart'; -import 'package:playground/utils/run_with_retry.dart'; +import '../util/run_with_retry.dart'; +import 'code_client/code_client.dart'; +import 'models/output_response.dart'; +import 'models/run_code_error.dart'; +import 'models/run_code_request.dart'; +import 'models/run_code_result.dart'; const kPipelineCheckDelay = Duration(seconds: 1); const kTimeoutErrorText = @@ -33,25 +33,25 @@ const kUnknownErrorText = 'Something went wrong. Please try again later or create a GitHub issue'; const kProcessingStartedText = 'The processing has started\n'; +// TODO(alexeyinkin): Rename. This is not a repository but a higher level client. class CodeRepository { - late final CodeClient _client; + final CodeClient _client; - CodeRepository(CodeClient client) { - _client = client; - } + CodeRepository({required CodeClient client,}): _client = client; - Stream runCode(RunCodeRequestWrapper request) async* { + Stream runCode(RunCodeRequest request) async* { try { - final initResult = RunCodeResult( + const initResult = RunCodeResult( status: RunCodeStatus.preparation, log: kProcessingStartedText, ); yield initResult; - var runCodeResponse = await _client.runCode(request); + + final runCodeResponse = await _client.runCode(request); final pipelineUuid = runCodeResponse.pipelineUuid; + yield* _checkPipelineExecution( pipelineUuid, - request, prevResult: initResult, ); } on RunCodeError catch (error) { @@ -68,26 +68,23 @@ class CodeRepository { } Stream _checkPipelineExecution( - String pipelineUuid, - RunCodeRequestWrapper request, { + String pipelineUuid, { RunCodeResult? prevResult, }) async* { try { final statusResponse = await runWithRetry( - () => _client.checkStatus(pipelineUuid, request), + () => _client.checkStatus(pipelineUuid), ); final result = await _getPipelineResult( pipelineUuid, statusResponse.status, prevResult, - request, ); yield result; if (!result.isFinished) { await Future.delayed(kPipelineCheckDelay); yield* _checkPipelineExecution( pipelineUuid, - request, prevResult: result, ); } @@ -105,17 +102,14 @@ class CodeRepository { String pipelineUuid, RunCodeStatus status, RunCodeResult? prevResult, - RunCodeRequestWrapper request, ) async { final prevOutput = prevResult?.output ?? ''; final prevLog = prevResult?.log ?? ''; final prevGraph = prevResult?.graph ?? ''; + switch (status) { case RunCodeStatus.compileError: - final compileOutput = await _client.getCompileOutput( - pipelineUuid, - request, - ); + final compileOutput = await _client.getCompileOutput(pipelineUuid); return RunCodeResult( pipelineUuid: pipelineUuid, status: status, @@ -123,6 +117,7 @@ class CodeRepository { log: prevLog, graph: prevGraph, ); + case RunCodeStatus.timeout: return RunCodeResult( pipelineUuid: pipelineUuid, @@ -132,8 +127,9 @@ class CodeRepository { log: prevLog, graph: prevGraph, ); + case RunCodeStatus.runError: - final output = await _client.getRunErrorOutput(pipelineUuid, request); + final output = await _client.getRunErrorOutput(pipelineUuid); return RunCodeResult( pipelineUuid: pipelineUuid, status: status, @@ -141,24 +137,27 @@ class CodeRepository { log: prevLog, graph: prevGraph, ); + case RunCodeStatus.validationError: final output = - await _client.getValidationErrorOutput(pipelineUuid, request); + await _client.getValidationErrorOutput(pipelineUuid); return RunCodeResult( status: status, output: output.output, log: prevLog, graph: prevGraph, ); + case RunCodeStatus.preparationError: final output = - await _client.getPreparationErrorOutput(pipelineUuid, request); + await _client.getPreparationErrorOutput(pipelineUuid); return RunCodeResult( status: status, output: output.output, log: prevLog, graph: prevGraph, ); + case RunCodeStatus.unknownError: return RunCodeResult( pipelineUuid: pipelineUuid, @@ -168,13 +167,14 @@ class CodeRepository { log: prevLog, graph: prevGraph, ); + case RunCodeStatus.executing: final responses = await Future.wait([ - _client.getRunOutput(pipelineUuid, request), - _client.getLogOutput(pipelineUuid, request), + _client.getRunOutput(pipelineUuid), + _client.getLogOutput(pipelineUuid), prevGraph.isEmpty - ? _client.getGraphOutput(pipelineUuid, request) - : Future.value(OutputResponse(prevGraph)), + ? _client.getGraphOutput(pipelineUuid) + : Future.value(OutputResponse(output: prevGraph)), ]); final output = responses[0]; final log = responses[1]; @@ -186,14 +186,15 @@ class CodeRepository { log: prevLog + log.output, graph: graph.output, ); + case RunCodeStatus.finished: final responses = await Future.wait([ - _client.getRunOutput(pipelineUuid, request), - _client.getLogOutput(pipelineUuid, request), - _client.getRunErrorOutput(pipelineUuid, request), + _client.getRunOutput(pipelineUuid), + _client.getLogOutput(pipelineUuid), + _client.getRunErrorOutput(pipelineUuid), prevGraph.isEmpty - ? _client.getGraphOutput(pipelineUuid, request) - : Future.value(OutputResponse(prevGraph)), + ? _client.getGraphOutput(pipelineUuid) + : Future.value(OutputResponse(output: prevGraph)), ]); final output = responses[0]; final log = responses[1]; @@ -206,6 +207,7 @@ class CodeRepository { log: prevLog + log.output, graph: graph.output, ); + default: return RunCodeResult( pipelineUuid: pipelineUuid, diff --git a/playground/frontend/playground_components/lib/src/repositories/example_client/example_client.dart b/playground/frontend/playground_components/lib/src/repositories/example_client/example_client.dart new file mode 100644 index 000000000000..d2bc5a792a93 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/example_client/example_client.dart @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../models/get_default_precompiled_object_request.dart'; +import '../models/get_precompiled_object_code_response.dart'; +import '../models/get_precompiled_object_request.dart'; +import '../models/get_precompiled_object_response.dart'; +import '../models/get_precompiled_objects_request.dart'; +import '../models/get_precompiled_objects_response.dart'; +import '../models/get_snippet_request.dart'; +import '../models/get_snippet_response.dart'; +import '../models/output_response.dart'; +import '../models/save_snippet_request.dart'; +import '../models/save_snippet_response.dart'; + +abstract class ExampleClient { + Future getPrecompiledObjects( + GetPrecompiledObjectsRequest request, + ); + + Future getPrecompiledObjectCode( + GetPrecompiledObjectRequest request, + ); + + Future getDefaultPrecompiledObject( + GetDefaultPrecompiledObjectRequest request, + ); + + Future getPrecompiledObject( + GetPrecompiledObjectRequest request, + ); + + Future getPrecompiledObjectOutput( + GetPrecompiledObjectRequest request, + ); + + Future getPrecompiledObjectLogs( + GetPrecompiledObjectRequest request, + ); + + Future getPrecompiledObjectGraph( + GetPrecompiledObjectRequest request, + ); + + Future getSnippet( + GetSnippetRequest request, + ); + + Future saveSnippet( + SaveSnippetRequest request, + ); +} diff --git a/playground/frontend/playground_components/lib/src/repositories/example_client/grpc_example_client.dart b/playground/frontend/playground_components/lib/src/repositories/example_client/grpc_example_client.dart new file mode 100644 index 000000000000..ceaa08a9bdf5 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/example_client/grpc_example_client.dart @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:grpc/grpc.dart'; + +import '../../api/iis_workaround_channel.dart'; +import '../../api/v1/api.pbgrpc.dart' as grpc; +import '../../models/category_with_examples.dart'; +import '../../models/example_base.dart'; +import '../../models/sdk.dart'; +import '../../util/replace_incorrect_symbols.dart'; +import '../models/get_default_precompiled_object_request.dart'; +import '../models/get_precompiled_object_code_response.dart'; +import '../models/get_precompiled_object_request.dart'; +import '../models/get_precompiled_object_response.dart'; +import '../models/get_precompiled_objects_request.dart'; +import '../models/get_precompiled_objects_response.dart'; +import '../models/get_snippet_request.dart'; +import '../models/get_snippet_response.dart'; +import '../models/output_response.dart'; +import '../models/save_snippet_request.dart'; +import '../models/save_snippet_response.dart'; +import '../models/shared_file.dart'; +import '../sdk_grpc_extension.dart'; +import 'example_client.dart'; + +class GrpcExampleClient implements ExampleClient { + final grpc.PlaygroundServiceClient _defaultClient; + + factory GrpcExampleClient({ + required String url, + }) { + final channel = IisWorkaroundChannel.xhr( + Uri.parse(url), + ); + + return GrpcExampleClient._( + client: grpc.PlaygroundServiceClient(channel), + ); + } + + GrpcExampleClient._({ + required grpc.PlaygroundServiceClient client, + }) : _defaultClient = client; + + @override + Future getPrecompiledObjects( + GetPrecompiledObjectsRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObjects( + _grpcGetPrecompiledObjectsRequest(request), + ), + ); + return GetPrecompiledObjectsResponse( + categories: _toClientCategories(response.sdkCategories), + ); + } + + @override + Future getDefaultPrecompiledObject( + GetDefaultPrecompiledObjectRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.getDefaultPrecompiledObject( + _grpcGetDefaultPrecompiledObjectRequest(request), + ), + ); + + return GetPrecompiledObjectResponse( + example: _toExampleModel( + request.sdk, + response.precompiledObject, + ), + ); + } + + @override + Future getPrecompiledObject( + GetPrecompiledObjectRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObject( + grpc.GetPrecompiledObjectRequest()..cloudPath = request.path, + ), + ); + + return GetPrecompiledObjectResponse( + example: _toExampleModel( + request.sdk, + response.precompiledObject, + ), + ); + } + + @override + Future getPrecompiledObjectCode( + GetPrecompiledObjectRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObjectCode( + _grpcGetPrecompiledObjectRequest(request), + ), + ); + + return GetPrecompiledObjectCodeResponse( + code: replaceIncorrectSymbols(response.code), + ); + } + + @override + Future getPrecompiledObjectOutput( + GetPrecompiledObjectRequest request, + ) async { + try { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObjectOutput( + _grpcGetPrecompiledObjectOutputRequest(request), + ), + ); + + return OutputResponse( + output: replaceIncorrectSymbols(response.output), + ); + } catch (ex) { + print(ex); + return OutputResponse( + output: '', + ); + } + } + + @override + Future getPrecompiledObjectLogs( + GetPrecompiledObjectRequest request, + ) async { + try { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObjectLogs( + _grpcGetPrecompiledObjectLogRequest(request), + ), + ); + + return OutputResponse( + output: replaceIncorrectSymbols(response.output), + ); + } catch (ex) { + print(ex); + return OutputResponse( + output: '', + ); + } + } + + @override + Future getPrecompiledObjectGraph( + GetPrecompiledObjectRequest request, + ) async { + try { + final response = await _runSafely( + () => _defaultClient.getPrecompiledObjectGraph( + _grpcGetPrecompiledGraphRequest(request), + ), + ); + + return OutputResponse( + output: response.graph, + ); + } catch (ex) { + print(ex); + return OutputResponse( + output: '', + ); + } + } + + @override + Future getSnippet( + GetSnippetRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.getSnippet( + _grpcGetSnippetRequest(request), + ), + ); + + return GetSnippetResponse( + files: _convertToSharedFileList(response.files), + sdk: response.sdk.model, + pipelineOptions: response.pipelineOptions, + ); + } + + @override + Future saveSnippet( + SaveSnippetRequest request, + ) async { + final response = await _runSafely( + () => _defaultClient.saveSnippet( + _grpcSaveSnippetRequest(request), + ), + ); + + return SaveSnippetResponse( + id: response.id, + ); + } + + Future _runSafely(Future Function() invoke) async { + try { + return await invoke(); + } on GrpcError catch (error) { + throw Exception(error.message); + } + } + + grpc.GetPrecompiledObjectsRequest _grpcGetPrecompiledObjectsRequest( + GetPrecompiledObjectsRequest request, + ) { + return grpc.GetPrecompiledObjectsRequest() + ..category = request.category ?? '' + ..sdk = request.sdk?.grpc ?? grpc.Sdk.SDK_UNSPECIFIED; + } + + grpc.GetDefaultPrecompiledObjectRequest + _grpcGetDefaultPrecompiledObjectRequest( + GetDefaultPrecompiledObjectRequest request, + ) { + return grpc.GetDefaultPrecompiledObjectRequest()..sdk = request.sdk.grpc; + } + + grpc.GetPrecompiledObjectCodeRequest _grpcGetPrecompiledObjectRequest( + GetPrecompiledObjectRequest request, + ) { + return grpc.GetPrecompiledObjectCodeRequest()..cloudPath = request.path; + } + + grpc.GetPrecompiledObjectOutputRequest _grpcGetPrecompiledObjectOutputRequest( + GetPrecompiledObjectRequest request, + ) { + return grpc.GetPrecompiledObjectOutputRequest()..cloudPath = request.path; + } + + grpc.GetPrecompiledObjectLogsRequest _grpcGetPrecompiledObjectLogRequest( + GetPrecompiledObjectRequest request, + ) { + return grpc.GetPrecompiledObjectLogsRequest()..cloudPath = request.path; + } + + grpc.GetPrecompiledObjectGraphRequest _grpcGetPrecompiledGraphRequest( + GetPrecompiledObjectRequest request, + ) { + return grpc.GetPrecompiledObjectGraphRequest()..cloudPath = request.path; + } + + grpc.GetSnippetRequest _grpcGetSnippetRequest( + GetSnippetRequest request, + ) { + return grpc.GetSnippetRequest()..id = request.id; + } + + grpc.SaveSnippetRequest _grpcSaveSnippetRequest( + SaveSnippetRequest request, + ) { + return grpc.SaveSnippetRequest() + ..sdk = request.sdk.grpc + ..pipelineOptions = request.pipelineOptions + ..files.addAll(_convertToSnippetFileList(request.files)); + } + + ExampleType _exampleTypeFromString(grpc.PrecompiledObjectType type) { + switch (type) { + case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_EXAMPLE: + return ExampleType.example; + case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_KATA: + return ExampleType.kata; + case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_UNIT_TEST: + return ExampleType.test; + case grpc.PrecompiledObjectType.PRECOMPILED_OBJECT_TYPE_UNSPECIFIED: + return ExampleType.all; + } + + return ExampleType.example; + } + + Map> _toClientCategories( + List response, + ) { + final result = >{}; + + for (final sdkMap in response) { + final sdk = sdkMap.sdk.model; + final categoriesForSdk = []; + + for (final category in sdkMap.categories) { + final examples = category.precompiledObjects + .map((example) => _toExampleModel(sdk, example)) + .toList(growable: false) + ..sort(); + + categoriesForSdk.add( + CategoryWithExamples( + title: category.categoryName, + examples: examples, + ), + ); + } + + result[sdk] = categoriesForSdk..sort(); + } + + return result; + } + + ExampleBase _toExampleModel(Sdk sdk, grpc.PrecompiledObject example) { + return ExampleBase( + sdk: sdk, + name: example.name, + description: example.description, + type: _exampleTypeFromString(example.type), + path: example.cloudPath, + contextLine: example.contextLine, + pipelineOptions: example.pipelineOptions, + isMultiFile: example.multifile, + link: example.link, + ); + } + + List _convertToSharedFileList( + List snippetFileList, + ) { + final sharedFilesList = []; + + for (final item in snippetFileList) { + sharedFilesList.add(SharedFile( + code: item.content, + isMain: item.isMain, + name: item.name, + )); + } + + return sharedFilesList; + } + + List _convertToSnippetFileList( + List sharedFilesList, + ) { + final snippetFileList = []; + + for (final item in sharedFilesList) { + snippetFileList.add( + grpc.SnippetFile() + ..name = item.name + ..isMain = true + ..content = item.code, + ); + } + + return snippetFileList; + } +} diff --git a/playground/frontend/playground_components/lib/src/repositories/example_repository.dart b/playground/frontend/playground_components/lib/src/repositories/example_repository.dart new file mode 100644 index 000000000000..9cb624a3464b --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/example_repository.dart @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../models/category_with_examples.dart'; +import '../models/example_base.dart'; +import '../models/sdk.dart'; +import 'example_client/example_client.dart'; +import 'models/get_default_precompiled_object_request.dart'; +import 'models/get_precompiled_object_request.dart'; +import 'models/get_precompiled_objects_request.dart'; +import 'models/get_snippet_request.dart'; +import 'models/get_snippet_response.dart'; +import 'models/save_snippet_request.dart'; + +class ExampleRepository { + final ExampleClient _client; + + ExampleRepository({ + required ExampleClient client, + }) : _client = client; + + Future>> getListOfExamples( + GetPrecompiledObjectsRequest request, + ) async { + final result = await _client.getPrecompiledObjects(request); + return result.categories; + } + + Future getDefaultExample( + GetDefaultPrecompiledObjectRequest request, + ) async { + final result = await _client.getDefaultPrecompiledObject(request); + return result.example; + } + + Future getExampleSource( + GetPrecompiledObjectRequest request, + ) async { + final result = await _client.getPrecompiledObjectCode(request); + return result.code; + } + + Future getExampleOutput( + GetPrecompiledObjectRequest request, + ) async { + final result = await _client.getPrecompiledObjectOutput(request); + return result.output; + } + + Future getExampleLogs( + GetPrecompiledObjectRequest request, + ) async { + final result = await _client.getPrecompiledObjectLogs(request); + return result.output; + } + + Future getExampleGraph( + GetPrecompiledObjectRequest request, + ) async { + final result = await _client.getPrecompiledObjectGraph(request); + return result.output; + } + + Future getExample( + GetPrecompiledObjectRequest request, + ) async { + final result = await _client.getPrecompiledObject(request); + return result.example; + } + + Future getSnippet( + GetSnippetRequest request, + ) async { + final result = await _client.getSnippet(request); + return result; + } + + Future saveSnippet( + SaveSnippetRequest request, + ) async { + final result = await _client.saveSnippet(request); + return result.id; + } +} diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/check_status_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/check_status_response.dart similarity index 87% rename from playground/frontend/lib/modules/editor/repository/code_repository/code_client/check_status_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/check_status_response.dart index 7e77419305ce..089a9a340bd4 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/check_status_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/check_status_response.dart @@ -16,10 +16,12 @@ * limitations under the License. */ -import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart'; +import 'run_code_result.dart'; class CheckStatusResponse { final RunCodeStatus status; - CheckStatusResponse(this.status); + const CheckStatusResponse({ + required this.status, + }); } diff --git a/playground/frontend/playground_components/lib/src/repositories/models/get_default_precompiled_object_request.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_default_precompiled_object_request.dart new file mode 100644 index 000000000000..4b42dee69518 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_default_precompiled_object_request.dart @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:equatable/equatable.dart'; + +import '../../models/sdk.dart'; + +class GetDefaultPrecompiledObjectRequest with EquatableMixin { + final Sdk sdk; + + const GetDefaultPrecompiledObjectRequest({ + required this.sdk, + }); + + @override + List get props => [ + sdk, + ]; +} diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_example_code_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_code_response.dart similarity index 87% rename from playground/frontend/lib/modules/examples/repositories/models/get_example_code_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_code_response.dart index c7a57e93f3f8..a7bbda2459d8 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/get_example_code_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_code_response.dart @@ -16,8 +16,10 @@ * limitations under the License. */ -class GetExampleCodeResponse { +class GetPrecompiledObjectCodeResponse { final String code; - GetExampleCodeResponse(this.code); + const GetPrecompiledObjectCodeResponse({ + required this.code, + }); } diff --git a/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_request.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_request.dart new file mode 100644 index 000000000000..95516e905a15 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_request.dart @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:equatable/equatable.dart'; + +import '../../models/sdk.dart'; + +/// A data object for multiple requests querying separate fields +/// of a given example. +class GetPrecompiledObjectRequest with EquatableMixin { + final String path; + final Sdk sdk; + + const GetPrecompiledObjectRequest({ + required this.path, + required this.sdk, + }); + + @override + List get props => [ + path, + sdk, + ]; +} diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_example_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_response.dart similarity index 81% rename from playground/frontend/lib/modules/examples/repositories/models/get_example_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_response.dart index 002c737a1a0a..0c6ce3d5b59e 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/get_example_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_object_response.dart @@ -16,10 +16,12 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_model.dart'; +import '../../models/example_base.dart'; -class GetExampleResponse { - final ExampleModel example; +class GetPrecompiledObjectResponse { + final ExampleBase example; - GetExampleResponse(this.example); + const GetPrecompiledObjectResponse({ + required this.example, + }); } diff --git a/playground/frontend/lib/modules/shortcuts/models/shortcut.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_request.dart similarity index 70% rename from playground/frontend/lib/modules/shortcuts/models/shortcut.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_request.dart index 0aafe060b052..c5c5b7246c31 100644 --- a/playground/frontend/lib/modules/shortcuts/models/shortcut.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_request.dart @@ -16,18 +16,22 @@ * limitations under the License. */ -import 'package:flutter/material.dart'; +import 'package:equatable/equatable.dart'; -class Shortcut { - final String name; - final LogicalKeySet shortcuts; - final Intent actionIntent; - final CallbackAction Function(BuildContext) createAction; +import '../../models/sdk.dart'; - Shortcut({ - required this.name, - required this.shortcuts, - required this.actionIntent, - required this.createAction, +class GetPrecompiledObjectsRequest with EquatableMixin { + final Sdk? sdk; + final String? category; + + const GetPrecompiledObjectsRequest({ + required this.sdk, + required this.category, }); + + @override + List get props => [ + sdk, + category, + ]; } diff --git a/playground/frontend/lib/configure_web.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_response.dart similarity index 76% rename from playground/frontend/lib/configure_web.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_response.dart index 5fa7eb6de546..69901df75c66 100644 --- a/playground/frontend/lib/configure_web.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_precompiled_objects_response.dart @@ -16,8 +16,13 @@ * limitations under the License. */ -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import '../../models/category_with_examples.dart'; +import '../../models/sdk.dart'; -void configureApp() { - setUrlStrategy(PathUrlStrategy()); +class GetPrecompiledObjectsResponse { + final Map> categories; + + const GetPrecompiledObjectsResponse({ + required this.categories, + }); } diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_snippet_request.dart similarity index 92% rename from playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_snippet_request.dart index 4319884981d9..610cf0d67b74 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/get_snippet_request.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_snippet_request.dart @@ -16,10 +16,10 @@ * limitations under the License. */ -class GetSnippetRequestWrapper { +class GetSnippetRequest { final String id; - const GetSnippetRequestWrapper({ + const GetSnippetRequest({ required this.id, }); } diff --git a/playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/get_snippet_response.dart similarity index 86% rename from playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/get_snippet_response.dart index 167f725a5fb8..9cb9c8ec1280 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/get_snippet_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/get_snippet_response.dart @@ -16,12 +16,12 @@ * limitations under the License. */ -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import '../../models/sdk.dart'; +import 'shared_file.dart'; class GetSnippetResponse { final List files; - final SDK sdk; + final Sdk sdk; final String pipelineOptions; const GetSnippetResponse({ diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/output_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/output_response.dart similarity index 87% rename from playground/frontend/lib/modules/editor/repository/code_repository/code_client/output_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/output_response.dart index c8835c707b23..d33292cf3333 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/output_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/output_response.dart @@ -16,8 +16,11 @@ * limitations under the License. */ +/// A common response for anything returning a single string. class OutputResponse { final String output; - OutputResponse(this.output); + const OutputResponse({ + required this.output, + }); } diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_error.dart b/playground/frontend/playground_components/lib/src/repositories/models/run_code_error.dart similarity index 94% rename from playground/frontend/lib/modules/editor/repository/code_repository/run_code_error.dart rename to playground/frontend/playground_components/lib/src/repositories/models/run_code_error.dart index b9339612ea58..68379a2473bb 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_error.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/run_code_error.dart @@ -19,5 +19,7 @@ class RunCodeError implements Exception { final String? message; - RunCodeError(this.message); + const RunCodeError({ + this.message, + }); } diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_request.dart b/playground/frontend/playground_components/lib/src/repositories/models/run_code_request.dart similarity index 88% rename from playground/frontend/lib/modules/editor/repository/code_repository/run_code_request.dart rename to playground/frontend/playground_components/lib/src/repositories/models/run_code_request.dart index aa09fcd0bffb..16a1e74df430 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_request.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/run_code_request.dart @@ -16,14 +16,14 @@ * limitations under the License. */ -import 'package:playground/modules/sdk/models/sdk.dart'; +import '../../models/sdk.dart'; -class RunCodeRequestWrapper { +class RunCodeRequest { final String code; - final SDK sdk; + final Sdk sdk; final Map pipelineOptions; - RunCodeRequestWrapper({ + const RunCodeRequest({ required this.code, required this.sdk, required this.pipelineOptions, diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/run_code_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/run_code_response.dart similarity index 93% rename from playground/frontend/lib/modules/editor/repository/code_repository/code_client/run_code_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/run_code_response.dart index 2fe480828f67..20f1a147972d 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/run_code_response.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/run_code_response.dart @@ -19,5 +19,7 @@ class RunCodeResponse { final String pipelineUuid; - RunCodeResponse(this.pipelineUuid); + const RunCodeResponse({ + required this.pipelineUuid, + }); } diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_result.dart b/playground/frontend/playground_components/lib/src/repositories/models/run_code_result.dart similarity index 75% rename from playground/frontend/lib/modules/editor/repository/code_repository/run_code_result.dart rename to playground/frontend/playground_components/lib/src/repositories/models/run_code_result.dart index 30a686e73721..5a3bcd876916 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/run_code_result.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/run_code_result.dart @@ -16,7 +16,7 @@ * limitations under the License. */ -import 'package:flutter/material.dart'; +import 'package:equatable/equatable.dart'; enum RunCodeStatus { unspecified, @@ -42,7 +42,7 @@ const kFinishedStatuses = [ RunCodeStatus.finished, ]; -class RunCodeResult { +class RunCodeResult with EquatableMixin { final RunCodeStatus status; final String? pipelineUuid; final String? output; @@ -50,7 +50,7 @@ class RunCodeResult { final String? graph; final String? errorMessage; - RunCodeResult({ + const RunCodeResult({ required this.status, this.pipelineUuid, this.output, @@ -64,20 +64,14 @@ class RunCodeResult { } @override - bool operator ==(Object other) => - identical(this, other) || - other is RunCodeResult && - runtimeType == other.runtimeType && - pipelineUuid == other.pipelineUuid && - status == other.status && - output == other.output && - log == other.log && - graph == other.graph && - errorMessage == other.errorMessage; - - @override - int get hashCode => - hashValues(pipelineUuid, status, output, log, errorMessage, graph); + List get props => [ + status, + pipelineUuid, + output, + log, + graph, + errorMessage, + ]; @override String toString() { diff --git a/playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart b/playground/frontend/playground_components/lib/src/repositories/models/save_snippet_request.dart similarity index 80% rename from playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart rename to playground/frontend/playground_components/lib/src/repositories/models/save_snippet_request.dart index c29ee4230f6a..4d64416efcc3 100644 --- a/playground/frontend/lib/modules/examples/repositories/models/save_snippet_request.dart +++ b/playground/frontend/playground_components/lib/src/repositories/models/save_snippet_request.dart @@ -16,15 +16,15 @@ * limitations under the License. */ -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import '../../models/sdk.dart'; +import 'shared_file.dart'; -class SaveSnippetRequestWrapper { +class SaveSnippetRequest { final List files; - final SDK sdk; + final Sdk sdk; final String pipelineOptions; - const SaveSnippetRequestWrapper({ + const SaveSnippetRequest({ required this.files, required this.sdk, required this.pipelineOptions, diff --git a/playground/frontend/lib/modules/examples/repositories/models/save_snippet_response.dart b/playground/frontend/playground_components/lib/src/repositories/models/save_snippet_response.dart similarity index 100% rename from playground/frontend/lib/modules/examples/repositories/models/save_snippet_response.dart rename to playground/frontend/playground_components/lib/src/repositories/models/save_snippet_response.dart diff --git a/playground/frontend/lib/modules/examples/repositories/models/shared_file_model.dart b/playground/frontend/playground_components/lib/src/repositories/models/shared_file.dart similarity index 100% rename from playground/frontend/lib/modules/examples/repositories/models/shared_file_model.dart rename to playground/frontend/playground_components/lib/src/repositories/models/shared_file.dart diff --git a/playground/frontend/lib/modules/shortcuts/utils/shortcuts_display_name.dart b/playground/frontend/playground_components/lib/src/repositories/sdk_grpc_extension.dart similarity index 53% rename from playground/frontend/lib/modules/shortcuts/utils/shortcuts_display_name.dart rename to playground/frontend/playground_components/lib/src/repositories/sdk_grpc_extension.dart index eff27c1c446c..d9c1b5863303 100644 --- a/playground/frontend/lib/modules/shortcuts/utils/shortcuts_display_name.dart +++ b/playground/frontend/playground_components/lib/src/repositories/sdk_grpc_extension.dart @@ -16,21 +16,36 @@ * limitations under the License. */ -import 'package:flutter/services.dart'; -import 'package:playground/modules/shortcuts/models/shortcut.dart'; -const kMetaKeyName = 'CMD/CTRL'; -const kShortcutKeyJoinSymbol = ' + '; +import '../api/v1/api.pbgrpc.dart' as g; +import '../models/sdk.dart'; -String getShortcutDisplayName(Shortcut shortcut) { - return shortcut.shortcuts.keys - .map((e) => getKeyDisplayName(e)) - .join(kShortcutKeyJoinSymbol); +extension SdkExtension on Sdk { + static final _idToGrpcEnum = { + Sdk.java.id: g.Sdk.SDK_JAVA, + Sdk.go.id: g.Sdk.SDK_GO, + Sdk.python.id: g.Sdk.SDK_PYTHON, + Sdk.scio.id: g.Sdk.SDK_SCIO, + }; + + g.Sdk get grpc => + _idToGrpcEnum[id] ?? + (throw Exception('SDK not supported for GRPS: $id')); } -String getKeyDisplayName(LogicalKeyboardKey e) { - if (e.keyId == LogicalKeyboardKey.meta.keyId) { - return kMetaKeyName; +extension GrpcSdkExtension on g.Sdk { + Sdk get model { + switch (this) { + case g.Sdk.SDK_JAVA: + return Sdk.java; + case g.Sdk.SDK_GO: + return Sdk.go; + case g.Sdk.SDK_PYTHON: + return Sdk.python; + case g.Sdk.SDK_SCIO: + return Sdk.scio; + } + + return Sdk(id: '$value', title: name); } - return e.keyLabel; } diff --git a/playground/frontend/playground_components/lib/src/theme/color_provider.dart b/playground/frontend/playground_components/lib/src/theme/color_provider.dart deleted file mode 100644 index c158229a53db..000000000000 --- a/playground/frontend/playground_components/lib/src/theme/color_provider.dart +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import '../constants/colors.dart'; - -class ThemeColorsProvider extends StatelessWidget { - final ThemeColors data; - final Widget child; - - const ThemeColorsProvider({ - super.key, - required this.data, - required this.child, - }); - - @override - Widget build(BuildContext context) { - return Provider.value( - value: data, - child: child, - ); - } -} - -class ThemeColors { - final Color? _background; - final bool isDark; - - ThemeColors({ - required this.isDark, - Color? background, - }) : _background = background; - - static ThemeColors of(BuildContext context, {bool listen = true}) { - return Provider.of(context, listen: listen); - } - - const ThemeColors.fromBrightness({ - required this.isDark, - }) : _background = null; - - Color get divider => - isDark ? BeamDarkThemeColors.grey : BeamLightThemeColors.grey; - - Color get primary => - isDark ? BeamDarkThemeColors.primary : BeamLightThemeColors.primary; - - Color get primaryBackgroundTextColor => BeamColors.white; - - Color get lightGreyBackgroundTextColor => BeamColors.black; - - Color get secondaryBackground => isDark - ? BeamDarkThemeColors.secondaryBackground - : BeamLightThemeColors.secondaryBackground; - - Color get background => - _background ?? - (isDark - ? BeamDarkThemeColors.primaryBackground - : BeamLightThemeColors.primaryBackground); - - Color get textColor => - isDark ? BeamDarkThemeColors.text : BeamLightThemeColors.text; -} diff --git a/playground/frontend/playground_components/lib/src/theme/switch_notifier.dart b/playground/frontend/playground_components/lib/src/theme/switch_notifier.dart index 06f7bcad5725..28fe46c58ded 100644 --- a/playground/frontend/playground_components/lib/src/theme/switch_notifier.dart +++ b/playground/frontend/playground_components/lib/src/theme/switch_notifier.dart @@ -20,23 +20,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'color_provider.dart'; - const kThemeMode = 'theme_mode'; class ThemeSwitchNotifier extends ChangeNotifier { ThemeMode themeMode = ThemeMode.light; - static const _darkThemeColors = ThemeColors.fromBrightness(isDark: true); - static const _lightThemeColors = ThemeColors.fromBrightness(isDark: false); - - ThemeColors get themeColors { - if (themeMode == ThemeMode.dark) { - return _darkThemeColors; - } - return _lightThemeColors; - } - void init() { _setPreferences(); } @@ -73,12 +61,7 @@ class ThemeSwitchNotifierProvider extends StatelessWidget { Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => ThemeSwitchNotifier()..init(), - child: Consumer( - builder: (context, themeSwitchNotifier, _) => ThemeColorsProvider( - data: themeSwitchNotifier.themeColors, - child: child, - ), - ), + child: child, ); } } diff --git a/playground/frontend/playground_components/lib/src/theme/theme.dart b/playground/frontend/playground_components/lib/src/theme/theme.dart index 88c644a0604c..9f6625316b94 100644 --- a/playground/frontend/playground_components/lib/src/theme/theme.dart +++ b/playground/frontend/playground_components/lib/src/theme/theme.dart @@ -16,43 +16,226 @@ * limitations under the License. */ +import 'package:code_text_field/code_text_field.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import '../../playground_components.dart'; +const codeFontSize = 14.0; + +class BeamThemeExtension extends ThemeExtension { + final Color borderColor; + final Color fieldBackgroundColor; + final Color iconColor; + final Color primaryBackgroundTextColor; + final Color lightGreyBackgroundTextColor; + final Color secondaryBackgroundColor; + + final TextStyle codeRootStyle; + final CodeThemeData codeTheme; + + const BeamThemeExtension({ + required this.borderColor, + required this.fieldBackgroundColor, + required this.iconColor, + required this.primaryBackgroundTextColor, + required this.lightGreyBackgroundTextColor, + required this.secondaryBackgroundColor, + required this.codeRootStyle, + required this.codeTheme, + }); + + @override + ThemeExtension copyWith({ + Color? borderColor, + Color? fieldBackgroundColor, + Color? iconColor, + Color? primaryBackgroundTextColor, + Color? lightGreyBackgroundTextColor, + Color? secondaryBackgroundColor, + TextStyle? codeRootStyle, + CodeThemeData? codeTheme, + }) { + return BeamThemeExtension( + borderColor: borderColor ?? this.borderColor, + fieldBackgroundColor: fieldBackgroundColor ?? this.fieldBackgroundColor, + iconColor: iconColor ?? this.iconColor, + primaryBackgroundTextColor: + primaryBackgroundTextColor ?? this.primaryBackgroundTextColor, + lightGreyBackgroundTextColor: + lightGreyBackgroundTextColor ?? this.lightGreyBackgroundTextColor, + secondaryBackgroundColor: + secondaryBackgroundColor ?? this.secondaryBackgroundColor, + codeRootStyle: codeRootStyle ?? this.codeRootStyle, + codeTheme: codeTheme ?? this.codeTheme, + ); + } + + @override + ThemeExtension lerp( + covariant BeamThemeExtension? other, + double t, + ) { + return BeamThemeExtension( + borderColor: Color.lerp(borderColor, other?.borderColor, t)!, + fieldBackgroundColor: + Color.lerp(fieldBackgroundColor, other?.fieldBackgroundColor, t)!, + iconColor: Color.lerp(iconColor, other?.iconColor, t)!, + primaryBackgroundTextColor: Color.lerp( + primaryBackgroundTextColor, other?.primaryBackgroundTextColor, t)!, + lightGreyBackgroundTextColor: Color.lerp(lightGreyBackgroundTextColor, + other?.lightGreyBackgroundTextColor, t)!, + secondaryBackgroundColor: Color.lerp( + secondaryBackgroundColor, other?.secondaryBackgroundColor, t)!, + codeRootStyle: TextStyle.lerp(codeRootStyle, other?.codeRootStyle, t)!, + codeTheme: t == 0.0 ? codeTheme : other?.codeTheme ?? codeTheme, + ); + } +} + final kLightTheme = ThemeData( brightness: Brightness.light, - primaryColor: BeamLightThemeColors.primary, + appBarTheme: _getAppBarTheme(BeamLightThemeColors.secondaryBackground), + backgroundColor: BeamLightThemeColors.primaryBackground, canvasColor: BeamLightThemeColors.primaryBackground, dividerColor: BeamLightThemeColors.grey, - scaffoldBackgroundColor: BeamLightThemeColors.secondaryBackground, - backgroundColor: BeamLightThemeColors.primaryBackground, - textTheme: _getTextTheme(BeamLightThemeColors.text), - textButtonTheme: _getTextButtonTheme(BeamLightThemeColors.text), + elevatedButtonTheme: _getElevatedButtonTheme(BeamLightThemeColors.primary), outlinedButtonTheme: _getOutlineButtonTheme( BeamLightThemeColors.text, BeamLightThemeColors.primary, ), - elevatedButtonTheme: _getElevatedButtonTheme(BeamLightThemeColors.primary), - appBarTheme: _getAppBarTheme(BeamLightThemeColors.secondaryBackground), + primaryColor: BeamLightThemeColors.primary, + scaffoldBackgroundColor: BeamLightThemeColors.secondaryBackground, + tabBarTheme: _getTabBarTheme( + textColor: BeamLightThemeColors.text, + indicatorColor: BeamLightThemeColors.primary, + ), + textButtonTheme: _getTextButtonTheme(BeamLightThemeColors.text), + textTheme: _getTextTheme(BeamLightThemeColors.text), + extensions: { + BeamThemeExtension( + borderColor: BeamLightThemeColors.border, + fieldBackgroundColor: BeamLightThemeColors.grey, + iconColor: BeamLightThemeColors.icon, + primaryBackgroundTextColor: BeamColors.white, + lightGreyBackgroundTextColor: BeamColors.black, + secondaryBackgroundColor: BeamLightThemeColors.secondaryBackground, + codeRootStyle: GoogleFonts.sourceCodePro( + backgroundColor: BeamLightThemeColors.primaryBackground, + color: BeamLightThemeColors.text, + fontSize: codeFontSize, + ), + codeTheme: CodeThemeData( + styles: { + 'root': TextStyle( + backgroundColor: BeamLightThemeColors.primaryBackground, + color: BeamLightThemeColors.text, + ), + 'comment': TextStyle(color: BeamLightThemeColors.codeComment), + 'quote': TextStyle(color: BeamLightThemeColors.code2), + 'variable': TextStyle(color: BeamLightThemeColors.code2), + 'keyword': TextStyle(color: BeamLightThemeColors.code2), + 'selector-tag': TextStyle(color: BeamLightThemeColors.code2), + 'built_in': TextStyle(color: BeamLightThemeColors.code2), + 'name': TextStyle(color: BeamLightThemeColors.code2), + 'tag': TextStyle(color: BeamLightThemeColors.code2), + 'string': TextStyle(color: BeamLightThemeColors.code1), + 'title': TextStyle(color: BeamLightThemeColors.code1), + 'section': TextStyle(color: BeamLightThemeColors.code1), + 'attribute': TextStyle(color: BeamLightThemeColors.code1), + 'literal': TextStyle(color: BeamLightThemeColors.code1), + 'template-tag': TextStyle(color: BeamLightThemeColors.code1), + 'template-variable': TextStyle(color: BeamLightThemeColors.code1), + 'type': TextStyle(color: BeamLightThemeColors.code1), + 'addition': TextStyle(color: BeamLightThemeColors.code1), + 'deletion': TextStyle(color: BeamLightThemeColors.code2), + 'selector-attr': TextStyle(color: BeamLightThemeColors.code2), + 'selector-pseudo': TextStyle(color: BeamLightThemeColors.code2), + 'meta': TextStyle(color: BeamLightThemeColors.code2), + 'doctag': TextStyle(color: BeamLightThemeColors.codeComment), + 'attr': TextStyle(color: BeamLightThemeColors.primary), + 'symbol': TextStyle(color: BeamLightThemeColors.code2), + 'bullet': TextStyle(color: BeamLightThemeColors.code2), + 'link': TextStyle(color: BeamLightThemeColors.code2), + 'emphasis': const TextStyle(fontStyle: FontStyle.italic), + 'strong': const TextStyle(fontWeight: FontWeight.bold), + }, + ), + ), + }, ); final kDarkTheme = ThemeData( brightness: Brightness.dark, - primaryColor: BeamDarkThemeColors.primary, + appBarTheme: _getAppBarTheme(BeamDarkThemeColors.secondaryBackground), + backgroundColor: BeamDarkThemeColors.primaryBackground, canvasColor: BeamDarkThemeColors.primaryBackground, dividerColor: BeamDarkThemeColors.grey, - scaffoldBackgroundColor: BeamDarkThemeColors.secondaryBackground, - backgroundColor: BeamDarkThemeColors.primaryBackground, - textTheme: _getTextTheme(BeamDarkThemeColors.text), - textButtonTheme: _getTextButtonTheme(BeamDarkThemeColors.text), + elevatedButtonTheme: _getElevatedButtonTheme(BeamDarkThemeColors.primary), outlinedButtonTheme: _getOutlineButtonTheme( BeamDarkThemeColors.text, BeamDarkThemeColors.primary, ), - elevatedButtonTheme: _getElevatedButtonTheme(BeamDarkThemeColors.primary), - appBarTheme: _getAppBarTheme(BeamDarkThemeColors.secondaryBackground), + primaryColor: BeamDarkThemeColors.primary, + scaffoldBackgroundColor: BeamDarkThemeColors.secondaryBackground, + tabBarTheme: _getTabBarTheme( + textColor: BeamDarkThemeColors.text, + indicatorColor: BeamDarkThemeColors.primary, + ), + textButtonTheme: _getTextButtonTheme(BeamDarkThemeColors.text), + textTheme: _getTextTheme(BeamDarkThemeColors.text), + extensions: { + BeamThemeExtension( + borderColor: BeamDarkThemeColors.border, + fieldBackgroundColor: BeamDarkThemeColors.grey, + iconColor: BeamDarkThemeColors.icon, + primaryBackgroundTextColor: BeamColors.white, + lightGreyBackgroundTextColor: BeamColors.black, + secondaryBackgroundColor: BeamDarkThemeColors.secondaryBackground, + codeRootStyle: GoogleFonts.sourceCodePro( + backgroundColor: BeamDarkThemeColors.primaryBackground, + color: BeamDarkThemeColors.text, + fontSize: codeFontSize, + ), + codeTheme: CodeThemeData( + styles: { + 'root': TextStyle( + backgroundColor: BeamDarkThemeColors.primaryBackground, + color: BeamDarkThemeColors.text, + ), + 'comment': TextStyle(color: BeamDarkThemeColors.codeComment), + 'quote': TextStyle(color: BeamDarkThemeColors.code2), + 'variable': TextStyle(color: BeamDarkThemeColors.code2), + 'keyword': TextStyle(color: BeamDarkThemeColors.code2), + 'selector-tag': TextStyle(color: BeamDarkThemeColors.code2), + 'built_in': TextStyle(color: BeamDarkThemeColors.code2), + 'name': TextStyle(color: BeamDarkThemeColors.code2), + 'tag': TextStyle(color: BeamDarkThemeColors.code2), + 'string': TextStyle(color: BeamDarkThemeColors.code1), + 'title': TextStyle(color: BeamDarkThemeColors.code1), + 'section': TextStyle(color: BeamDarkThemeColors.code1), + 'attribute': TextStyle(color: BeamDarkThemeColors.code1), + 'literal': TextStyle(color: BeamDarkThemeColors.code1), + 'template-tag': TextStyle(color: BeamDarkThemeColors.code1), + 'template-variable': TextStyle(color: BeamDarkThemeColors.code1), + 'type': TextStyle(color: BeamDarkThemeColors.code1), + 'addition': TextStyle(color: BeamDarkThemeColors.code1), + 'deletion': TextStyle(color: BeamDarkThemeColors.code2), + 'selector-attr': TextStyle(color: BeamDarkThemeColors.code2), + 'selector-pseudo': TextStyle(color: BeamDarkThemeColors.code2), + 'meta': TextStyle(color: BeamDarkThemeColors.code2), + 'doctag': TextStyle(color: BeamDarkThemeColors.codeComment), + 'attr': TextStyle(color: BeamDarkThemeColors.primary), + 'symbol': TextStyle(color: BeamDarkThemeColors.code2), + 'bullet': TextStyle(color: BeamDarkThemeColors.code2), + 'link': TextStyle(color: BeamDarkThemeColors.code2), + 'emphasis': const TextStyle(fontStyle: FontStyle.italic), + 'strong': const TextStyle(fontWeight: FontWeight.bold), + }, + ), + ), + }, ); TextTheme _getTextTheme(Color textColor) { @@ -87,7 +270,7 @@ TextTheme _getTextTheme(Color textColor) { titleMedium: _emptyTextStyle, titleSmall: _emptyTextStyle, labelLarge: TextStyle( - fontSize: 16, + fontSize: 14, fontWeight: FontWeight.w600, ), labelMedium: _emptyTextStyle, @@ -139,8 +322,22 @@ ElevatedButtonThemeData _getElevatedButtonTheme(Color color) { style: ElevatedButton.styleFrom( foregroundColor: BeamColors.white, backgroundColor: color, - padding: _buttonPadding, - elevation: BeamSizes.size0, + ), + ); +} + +TabBarTheme _getTabBarTheme({ + required Color textColor, + required Color indicatorColor, +}) { + const labelStyle = TextStyle(fontWeight: FontWeight.w600); + return TabBarTheme( + unselectedLabelColor: textColor, + labelColor: textColor, + labelStyle: labelStyle, + unselectedLabelStyle: labelStyle, + indicator: UnderlineTabIndicator( + borderSide: BorderSide(width: 2.0, color: indicatorColor), ), ); } diff --git a/playground/frontend/lib/modules/editor/parsers/run_options_parser.dart b/playground/frontend/playground_components/lib/src/util/pipeline_options.dart similarity index 100% rename from playground/frontend/lib/modules/editor/parsers/run_options_parser.dart rename to playground/frontend/playground_components/lib/src/util/pipeline_options.dart diff --git a/playground/frontend/lib/utils/replace_incorrect_symbols.dart b/playground/frontend/playground_components/lib/src/util/replace_incorrect_symbols.dart similarity index 87% rename from playground/frontend/lib/utils/replace_incorrect_symbols.dart rename to playground/frontend/playground_components/lib/src/util/replace_incorrect_symbols.dart index 08d75ee7dc55..69a0bb684009 100644 --- a/playground/frontend/lib/utils/replace_incorrect_symbols.dart +++ b/playground/frontend/playground_components/lib/src/util/replace_incorrect_symbols.dart @@ -16,11 +16,9 @@ * limitations under the License. */ -const kIncorrectTabSymbol = ' '; -const kTabSymbolReplacement = ' '; - +// TODO(alexeyinkin): Move to the editor, https://github.com/apache/beam/issues/23079 /// sometimes code contains incorrect symbols (like tab which doesn't look properly), /// replace it with correct ones String replaceIncorrectSymbols(String output) { - return output.replaceAll(kIncorrectTabSymbol, kTabSymbolReplacement); + return output.replaceAll('\t', ' '); } diff --git a/playground/frontend/lib/utils/run_with_retry.dart b/playground/frontend/playground_components/lib/src/util/run_with_retry.dart similarity index 100% rename from playground/frontend/lib/utils/run_with_retry.dart rename to playground/frontend/playground_components/lib/src/util/run_with_retry.dart diff --git a/playground/frontend/playground_components/lib/src/widgets/bubble.dart b/playground/frontend/playground_components/lib/src/widgets/bubble.dart new file mode 100644 index 000000000000..1f6e46a1848a --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/bubble.dart @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; +import 'package:playground_components/playground_components.dart'; + +import '../constants/sizes.dart'; + +class BubbleWidget extends StatelessWidget { + final bool isSelected; + final VoidCallback onTap; + final String title; + + const BubbleWidget({ + super.key, + required this.isSelected, + required this.onTap, + required this.title, + }); + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + final ext = themeData.extension()!; + + return MouseRegion( + cursor: SystemMouseCursors.click, + child: Padding( + padding: const EdgeInsets.only(right: BeamSizes.size8), + child: GestureDetector( + onTap: onTap, + child: Container( + height: BeamSizes.buttonHeight, + padding: const EdgeInsets.symmetric(horizontal: BeamSizes.size16), + decoration: BoxDecoration( + color: isSelected ? themeData.primaryColor : ext.borderColor, + borderRadius: BorderRadius.circular(BeamBorderRadius.infinite), + ), + child: Center( + child: Text( + title, + style: TextStyle( + color: isSelected + ? ext.primaryBackgroundTextColor + : ext.lightGreyBackgroundTextColor, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/playground/frontend/playground_components/lib/src/widgets/drag_indicator.dart b/playground/frontend/playground_components/lib/src/widgets/drag_handle.dart similarity index 69% rename from playground/frontend/playground_components/lib/src/widgets/drag_indicator.dart rename to playground/frontend/playground_components/lib/src/widgets/drag_handle.dart index 768b513b9e33..7dfed2c589ae 100644 --- a/playground/frontend/playground_components/lib/src/widgets/drag_indicator.dart +++ b/playground/frontend/playground_components/lib/src/widgets/drag_handle.dart @@ -19,17 +19,25 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import '../constants/names.dart'; +import '../constants/playground_components.dart'; import '../generated/assets.gen.dart'; -class DragIndicator extends StatelessWidget { - const DragIndicator(); +class DragHandle extends StatelessWidget { + final Axis direction; + + const DragHandle({ + required this.direction, + }); @override Widget build(BuildContext context) { + // TODO: Use a single file and just rotate it if needed. + // Currently it gets blurred in HTML renderer. Find a fix. return SvgPicture.asset( - Assets.svg.drag, - package: BeamNames.package, + direction == Axis.horizontal + ? Assets.svg.dragHorizontal + : Assets.svg.dragVertical, + package: PlaygroundComponents.packageName, ); } } diff --git a/playground/frontend/lib/modules/editor/components/editor_textarea.dart b/playground/frontend/playground_components/lib/src/widgets/editor_textarea.dart similarity index 80% rename from playground/frontend/lib/modules/editor/components/editor_textarea.dart rename to playground/frontend/playground_components/lib/src/widgets/editor_textarea.dart index d3e7d3b6bda8..05231d0ee8b7 100644 --- a/playground/frontend/lib/modules/editor/components/editor_textarea.dart +++ b/playground/frontend/playground_components/lib/src/widgets/editor_textarea.dart @@ -16,14 +16,14 @@ * limitations under the License. */ +// TODO(alexeyinkin): Refactor this, merge into snippet_editor.dart + import 'package:code_text_field/code_text_field.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/fonts.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; + +import '../models/example.dart'; +import '../models/sdk.dart'; +import '../theme/theme.dart'; const kJavaRegExp = r'import\s[A-z.0-9]*\;\n\n[(\/\*\*)|(public)|(class)]'; const kPythonRegExp = r'[^\S\r\n](import|as)[^\S\r\n][A-z]*\n\n'; @@ -36,21 +36,21 @@ const kAdditionalLinesForScrolling = 4; class EditorTextArea extends StatefulWidget { final CodeController codeController; - final SDK sdk; - final ExampleModel? example; + final Sdk sdk; + final Example? example; final bool enabled; final bool isEditable; - final bool isEmbedded; + final bool goToContextLine; const EditorTextArea({ - Key? key, + super.key, required this.codeController, required this.sdk, this.example, required this.enabled, required this.isEditable, - this.isEmbedded = false, - }) : super(key: key); + required this.goToContextLine, + }); @override State createState() => _EditorTextAreaState(); @@ -68,32 +68,30 @@ class _EditorTextAreaState extends State { @override Widget build(BuildContext context) { - if (!widget.isEmbedded) { + if (widget.goToContextLine) { WidgetsBinding.instance.addPostFrameCallback((_) => _setTextScrolling()); } + final ext = Theme.of(context).extension()!; + return Semantics( container: true, textField: true, multiline: true, enabled: widget.enabled, readOnly: widget.enabled, - label: AppLocalizations.of(context)!.codeTextArea, + label: 'widgets.codeEditor.label', child: FocusScope( node: FocusScopeNode(canRequestFocus: widget.isEditable), - child: CodeField( - key: codeFieldKey, - focusNode: focusNode, - enabled: widget.enabled, - controller: widget.codeController, - textStyle: getCodeFontStyle( - textStyle: const TextStyle(fontSize: kCodeFontSize), - ), - expands: true, - lineNumberStyle: LineNumberStyle( - textStyle: TextStyle( - color: ThemeColors.of(context).grey1Color, - ), + child: CodeTheme( + data: ext.codeTheme, + child: CodeField( + key: codeFieldKey, + focusNode: focusNode, + enabled: widget.enabled, + controller: widget.codeController, + textStyle: ext.codeRootStyle, + expands: true, ), ), ), @@ -143,7 +141,7 @@ class _EditorTextAreaState extends State { codeFieldKey.currentContext?.findRenderObject() as RenderBox; double height = rBox.size.height * .75; - return height ~/ kCodeFontSize; + return height ~/ codeFontSize; } int _getIndexOfContextLine() { diff --git a/playground/frontend/lib/modules/actions/components/header_icon_button.dart b/playground/frontend/playground_components/lib/src/widgets/header_icon_button.dart similarity index 91% rename from playground/frontend/lib/modules/actions/components/header_icon_button.dart rename to playground/frontend/playground_components/lib/src/widgets/header_icon_button.dart index 09d42e8aa187..01e4a62de1c8 100644 --- a/playground/frontend/lib/modules/actions/components/header_icon_button.dart +++ b/playground/frontend/playground_components/lib/src/widgets/header_icon_button.dart @@ -17,7 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/constants/sizes.dart'; + +import '../constants/sizes.dart'; class HeaderIconButton extends StatelessWidget { final VoidCallback onPressed; @@ -25,16 +26,16 @@ class HeaderIconButton extends StatelessWidget { final Widget icon; const HeaderIconButton({ - Key? key, + super.key, required this.onPressed, required this.label, required this.icon, - }) : super(key: key); + }); @override Widget build(BuildContext context) { return SizedBox( - height: kHeaderButtonHeight, + height: BeamSizes.headerButtonHeight, child: TextButton.icon( icon: icon, label: Text(label), diff --git a/playground/frontend/lib/components/loading_indicator/loading_indicator.dart b/playground/frontend/playground_components/lib/src/widgets/loading_indicator.dart similarity index 83% rename from playground/frontend/lib/components/loading_indicator/loading_indicator.dart rename to playground/frontend/playground_components/lib/src/widgets/loading_indicator.dart index 8d7fb142a28d..60b696ed880f 100644 --- a/playground/frontend/lib/components/loading_indicator/loading_indicator.dart +++ b/playground/frontend/playground_components/lib/src/widgets/loading_indicator.dart @@ -17,12 +17,16 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/constants/colors.dart'; + +import '../constants/sizes.dart'; class LoadingIndicator extends StatelessWidget { final double size; - const LoadingIndicator({Key? key, required this.size}) : super(key: key); + const LoadingIndicator({ + super.key, + this.size = BeamSizes.loadingIndicator, + }); @override Widget build(BuildContext context) { @@ -30,8 +34,8 @@ class LoadingIndicator extends StatelessWidget { child: SizedBox( height: size, width: size, - child: const CircularProgressIndicator( - color: kLightPrimary, + child: CircularProgressIndicator( + color: Theme.of(context).primaryColor, ), ), ); diff --git a/playground/frontend/playground_components/lib/src/widgets/logo.dart b/playground/frontend/playground_components/lib/src/widgets/logo.dart index 61741807e3c6..2a0bdad9519b 100644 --- a/playground/frontend/playground_components/lib/src/widgets/logo.dart +++ b/playground/frontend/playground_components/lib/src/widgets/logo.dart @@ -18,7 +18,7 @@ import 'package:flutter/material.dart'; -import '../constants/names.dart'; +import '../constants/playground_components.dart'; import '../constants/sizes.dart'; import '../generated/assets.gen.dart'; @@ -30,7 +30,7 @@ class BeamLogo extends StatelessWidget { return Image.asset( Assets.png.beamLogo.path, height: BeamIconSizes.large, - package: BeamNames.package, + package: PlaygroundComponents.packageName, ); } } diff --git a/playground/frontend/lib/modules/output/components/graph.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph.dart similarity index 83% rename from playground/frontend/lib/modules/output/components/graph.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph.dart index 01fcfbd4b08e..6f5e95c01d24 100644 --- a/playground/frontend/lib/modules/output/components/graph.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/graph/graph_builder/canvas_drawer.dart'; -import 'package:playground/modules/graph/graph_builder/graph_builder.dart'; -import 'package:playground/modules/graph/graph_builder/painters/graph_painter.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; + +import 'graph_builder/canvas_drawer.dart'; +import 'graph_builder/graph_builder.dart'; +import 'graph_builder/painters/graph_painter.dart'; class GraphCustomPainter extends CustomPainter { final GraphPainter graph; @@ -41,15 +41,15 @@ class GraphCustomPainter extends CustomPainter { class GraphTab extends StatefulWidget { final String graph; - final SDK sdk; - final GraphDirection direction; + final Sdk sdk; + final Axis direction; const GraphTab({ - Key? key, + super.key, required this.graph, required this.sdk, required this.direction, - }) : super(key: key); + }); @override State createState() => _GraphTabState(); @@ -88,9 +88,8 @@ class _GraphTabState extends State { return Container(); } return Padding( - padding: const EdgeInsets.all(kXlSpacing), + padding: const EdgeInsets.all(BeamSizes.size16), child: SingleChildScrollView( - scrollDirection: Axis.vertical, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: ClipRRect( diff --git a/playground/frontend/lib/modules/graph/graph_builder/canvas_drawer.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/canvas_drawer.dart similarity index 93% rename from playground/frontend/lib/modules/graph/graph_builder/canvas_drawer.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/canvas_drawer.dart index 59cbda7bb848..65a95919021e 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/canvas_drawer.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/canvas_drawer.dart @@ -20,7 +20,8 @@ import 'dart:ui'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; -import 'package:playground/constants/colors.dart'; + +import '../../../../constants/colors.dart'; const kDashSize = 4; const kArrowSize = 4; @@ -32,13 +33,13 @@ class CanvasDrawer { CanvasDrawer(this.canvas); final borderPaint = Paint() - ..color = kLightGrey2 + ..color = BeamGraphColors.border ..strokeWidth = 1 ..isAntiAlias = true ..style = PaintingStyle.fill; final linePaint = Paint() - ..color = kLightPrimary + ..color = BeamGraphColors.edge ..strokeWidth = 2 ..isAntiAlias = true ..style = PaintingStyle.fill; @@ -71,7 +72,7 @@ class CanvasDrawer { createParagraph( text, width, - color: kLightGrey1, + color: BeamGraphColors.node, ), offset, ); @@ -82,7 +83,6 @@ class CanvasDrawer { } drawDashedLine(double x1, double y1, double x2, double y2) { - double startX = x1; double startY = y1; @@ -123,12 +123,12 @@ class CanvasDrawer { } drawRect( - double left, - double top, - double width, - double height, - double radius, - ) { + double left, + double top, + double width, + double height, + double radius, + ) { final borderRadius = Radius.circular(radius); final rect = Rect.fromLTWH(left, top, width, height); final rRect = RRect.fromRectAndRadius(rect, borderRadius); diff --git a/playground/frontend/lib/modules/graph/graph_builder/extractors/edge_extractor.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/edge_extractor.dart similarity index 93% rename from playground/frontend/lib/modules/graph/graph_builder/extractors/edge_extractor.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/edge_extractor.dart index 1c7685d45cb3..27c62de3177b 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/extractors/edge_extractor.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/edge_extractor.dart @@ -16,8 +16,8 @@ * limitations under the License. */ -import 'package:playground/modules/graph/graph_builder/extractors/extractors.dart'; -import 'package:playground/modules/graph/models/graph.dart'; +import '../../models/graph.dart'; +import 'extractors.dart'; final RegExp kEdgeRegExp = RegExp(r'''.+ -> .+'''); const kPrimaryEdgeStyle = 'solid'; diff --git a/playground/frontend/lib/modules/graph/graph_builder/extractors/element_extractor.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/element_extractor.dart similarity index 90% rename from playground/frontend/lib/modules/graph/graph_builder/extractors/element_extractor.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/element_extractor.dart index d533e2d230fe..e4af2ec9bab9 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/extractors/element_extractor.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/element_extractor.dart @@ -16,9 +16,9 @@ * limitations under the License. */ -import 'package:playground/modules/graph/graph_builder/extractors/extractor_utils.dart'; -import 'package:playground/modules/graph/graph_builder/extractors/extractors.dart'; -import 'package:playground/modules/graph/models/graph.dart'; +import '../../models/graph.dart'; +import 'extractor_utils.dart'; +import 'extractors.dart'; final RegExp kGraphElementRegExp = RegExp(r'''subgraph cluster_0'''); final RegExp kSubgraphElementRegExp = RegExp(r'''subgraph cluster_\d+'''); diff --git a/playground/frontend/lib/modules/graph/graph_builder/extractors/extractor_utils.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/extractor_utils.dart similarity index 100% rename from playground/frontend/lib/modules/graph/graph_builder/extractors/extractor_utils.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/extractor_utils.dart diff --git a/playground/frontend/lib/modules/graph/graph_builder/extractors/extractors.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/extractors.dart similarity index 100% rename from playground/frontend/lib/modules/graph/graph_builder/extractors/extractors.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/extractors.dart diff --git a/playground/frontend/lib/modules/graph/graph_builder/extractors/label_extractor.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/label_extractor.dart similarity index 93% rename from playground/frontend/lib/modules/graph/graph_builder/extractors/label_extractor.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/label_extractor.dart index 1c04558c594a..1c9ce981388e 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/extractors/label_extractor.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/extractors/label_extractor.dart @@ -16,7 +16,7 @@ * limitations under the License. */ -import 'package:playground/modules/graph/graph_builder/extractors/extractors.dart'; +import 'extractors.dart'; final RegExp kLabelRegExp = RegExp(r'''label = ".*"'''); const kLabelStart = 'label = "'; diff --git a/playground/frontend/lib/modules/graph/graph_builder/graph_builder.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/graph_builder.dart similarity index 85% rename from playground/frontend/lib/modules/graph/graph_builder/graph_builder.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/graph_builder.dart index 53901aaa9500..9549a76fa601 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/graph_builder.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/graph_builder.dart @@ -18,15 +18,17 @@ import 'dart:convert'; -import 'package:playground/modules/graph/graph_builder/extractors/edge_extractor.dart'; -import 'package:playground/modules/graph/graph_builder/extractors/element_extractor.dart'; -import 'package:playground/modules/graph/graph_builder/extractors/label_extractor.dart'; -import 'package:playground/modules/graph/graph_builder/painters/edge_painter.dart'; -import 'package:playground/modules/graph/graph_builder/painters/graph_painter.dart'; -import 'package:playground/modules/graph/graph_builder/painters/node_painter.dart'; -import 'package:playground/modules/graph/models/graph.dart'; -import 'package:playground/modules/graph/models/table_cell.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:playground_components/playground_components.dart'; + +import '../models/graph.dart'; +import '../models/table_cell.dart'; +import 'extractors/edge_extractor.dart'; +import 'extractors/element_extractor.dart'; +import 'extractors/label_extractor.dart'; +import 'painters/edge_painter.dart'; +import 'painters/graph_painter.dart'; +import 'painters/node_painter.dart'; final kGraphElementExtractor = GraphElementExtractor(); final kLabelExtractor = LabelExtractor(); @@ -37,20 +39,23 @@ abstract class GraphBuilder { final List edges = []; final Map elementsMap = {}; - static GraphBuilder? parseDot(String dot, SDK sdk) { + // TODO(alexeyinkin): Use this as the source of truth + // of whether a graph is available for an SDK, + // https://github.com/apache/beam/issues/23251 + static final _graphBuilderFactoriesBySdk = { + Sdk.java: JavaGraphBuilder.new, + Sdk.python: PythonGraphBuilder.new, + }; + + static GraphBuilder? parseDot(String dot, Sdk sdk) { LineSplitter ls = const LineSplitter(); List lines = ls.convert(dot); - GraphBuilder builder; - switch (sdk) { - case SDK.java: - builder = JavaGraphBuilder(); - break; - case SDK.python: - builder = PythonGraphBuilder(); - break; - default: - return null; + final builder = _graphBuilderFactoriesBySdk[sdk]?.call(); + + if (builder == null) { + return null; } + for (var line in lines) { builder.parseNextLine(line); } @@ -60,7 +65,7 @@ abstract class GraphBuilder { void parseNextLine(String line); - GraphPainter getPainter(GraphDirection direction) { + GraphPainter getPainter(widgets.Axis direction) { final List nodeElements = elements .where((element) => element.type == NodeType.node) .toList() @@ -86,9 +91,9 @@ abstract class GraphBuilder { .map((element) { final cell = nodeToCellMap[element.name]!; final row = - direction == GraphDirection.horizontal ? cell.row : cell.column; + direction == widgets.Axis.horizontal ? cell.row : cell.column; final column = - direction == GraphDirection.horizontal ? cell.column : cell.row; + direction == widgets.Axis.horizontal ? cell.column : cell.row; return NodeElementPainter( element: element as Node, row: row, diff --git a/playground/frontend/lib/modules/graph/graph_builder/painters/edge_painter.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/edge_painter.dart similarity index 90% rename from playground/frontend/lib/modules/graph/graph_builder/painters/edge_painter.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/edge_painter.dart index d67610530104..93a3d15d9108 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/painters/edge_painter.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/edge_painter.dart @@ -18,13 +18,14 @@ import 'dart:math'; import 'package:collection/collection.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/graph/graph_builder/canvas_drawer.dart'; -import 'package:playground/modules/graph/graph_builder/painters/graph_painter.dart'; -import 'package:playground/modules/graph/graph_builder/painters/node_painter.dart'; -import 'package:playground/modules/graph/models/graph.dart'; +import 'package:flutter/widgets.dart'; -const kEdgeSpacing = 2 * kXlSpacing; +import '../../../../../constants/sizes.dart'; +import '../../models/graph.dart'; +import '../canvas_drawer.dart'; +import 'node_painter.dart'; + +const kEdgeSpacing = 2 * BeamSizes.size16; class EdgePainter { final Edge edge; @@ -38,9 +39,9 @@ class EdgePainter { Map columnStarts, Map rowSizes, Map columnSizes, - GraphDirection direction, + Axis direction, ) { - if (direction == GraphDirection.vertical) { + if (direction == Axis.vertical) { _drawVertical( drawer, elementsMap, rowStarts, columnStarts, rowSizes, columnSizes); } else { @@ -109,7 +110,7 @@ class EdgePainter { }); drawer.drawRightArrow( - optimizedMovePoints[0].x + kXlSpacing, optimizedMovePoints[0].y); + optimizedMovePoints[0].x + BeamSizes.size16, optimizedMovePoints[0].y); _drawLine(drawer, optimizedMovePoints); } @@ -174,7 +175,7 @@ class EdgePainter { }); drawer.drawBottomArrow( - optimizedMovePoints[0].x, optimizedMovePoints[0].y + kXlSpacing); + optimizedMovePoints[0].x, optimizedMovePoints[0].y + BeamSizes.size16); _drawLine(drawer, optimizedMovePoints); } diff --git a/playground/frontend/lib/modules/graph/graph_builder/painters/graph_painter.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/graph_painter.dart similarity index 83% rename from playground/frontend/lib/modules/graph/graph_builder/painters/graph_painter.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/graph_painter.dart index 81d563531630..8c2c4c356c02 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/painters/graph_painter.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/graph_painter.dart @@ -19,18 +19,17 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/graph/graph_builder/canvas_drawer.dart'; -import 'package:playground/modules/graph/graph_builder/painters/edge_painter.dart'; -import 'package:playground/modules/graph/graph_builder/painters/node_painter.dart'; -import 'package:playground/modules/graph/models/graph.dart'; -enum GraphDirection { vertical, horizontal } +import '../../../../../constants/sizes.dart'; +import '../../models/graph.dart'; +import '../canvas_drawer.dart'; +import 'edge_painter.dart'; +import 'node_painter.dart'; class GraphPainter { final List elementsPainter; final List edges; - final GraphDirection direction; + final Axis direction; final Map elementsMap = {}; final Map rowSizes = {}; final Map columnSizes = {}; @@ -41,8 +40,8 @@ class GraphPainter { final lastColumn = columnStarts.length - 1; final lastRow = rowStarts.length - 1; final width = - columnStarts[lastColumn]! + columnSizes[lastColumn]! + 4 * kXlSpacing; - final height = rowStarts[lastRow]! + rowSizes[lastRow]! + 4 * kXlSpacing; + columnStarts[lastColumn]! + columnSizes[lastColumn]! + 4 * BeamSizes.size16; + final height = rowStarts[lastRow]! + rowSizes[lastRow]! + 4 * BeamSizes.size16; return Size(width, height); } @@ -70,11 +69,11 @@ class GraphPainter { var top = 0.0; for (var r = 0; r < rowSizes.length; r++) { rowStarts[r] = top; - top = top + rowSizes[r]! + 4 * kXlSpacing; + top = top + rowSizes[r]! + 4 * BeamSizes.size16; } for (var c = 0; c < columnSizes.length; c++) { columnStarts[c] = left; - left = left + columnSizes[c]! + 4 * kXlSpacing; + left = left + columnSizes[c]! + 4 * BeamSizes.size16; } } diff --git a/playground/frontend/lib/modules/graph/graph_builder/painters/node_painter.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/node_painter.dart similarity index 82% rename from playground/frontend/lib/modules/graph/graph_builder/painters/node_painter.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/node_painter.dart index 6e04f880bddb..f0b78632d99a 100644 --- a/playground/frontend/lib/modules/graph/graph_builder/painters/node_painter.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/graph/graph_builder/painters/node_painter.dart @@ -15,12 +15,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/graph/graph_builder/canvas_drawer.dart'; -import 'package:playground/modules/graph/models/graph.dart'; + +import '../../../../../constants/sizes.dart'; +import '../../models/graph.dart'; +import '../canvas_drawer.dart'; class NodeElementPainter { int row; @@ -47,18 +49,18 @@ class NodeElementPainter { drawer.drawText( parentLabel, maxTextWidth, - Offset(left! + kXlSpacing, top! + kLgSpacing), + Offset(left! + BeamSizes.size16, top! + BeamSizes.size12), ); drawer.drawSecondaryText( element.label, maxTextWidth, - Offset(left! + kXlSpacing, top! + kLgSpacing + kMdSpacing + 10.0), + Offset(left! + BeamSizes.size16, top! + BeamSizes.size12 + BeamSizes.size8 + 10.0), ); } else { drawer.drawText( element.label, maxTextWidth, - Offset(left! + kXlSpacing, top! + (56 / 2 - 5)), + Offset(left! + BeamSizes.size16, top! + (56 / 2 - 5)), ); } } @@ -75,8 +77,8 @@ class NodeElementPainter { if (size != null) { return size!; } - final fullWidth = maxTextWidth + kXlSpacing * 2; - size = Size(fullWidth, kLgSpacing * 2 + kMdSpacing + 10.0 * 2); + final fullWidth = maxTextWidth + BeamSizes.size16 * 2; + size = Size(fullWidth, BeamSizes.size12 * 2 + BeamSizes.size8 + 10.0 * 2); return size!; } diff --git a/playground/frontend/lib/modules/graph/models/graph.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/models/graph.dart similarity index 100% rename from playground/frontend/lib/modules/graph/models/graph.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/models/graph.dart diff --git a/playground/frontend/lib/modules/graph/models/table_cell.dart b/playground/frontend/playground_components/lib/src/widgets/output/graph/models/table_cell.dart similarity index 100% rename from playground/frontend/lib/modules/graph/models/table_cell.dart rename to playground/frontend/playground_components/lib/src/widgets/output/graph/models/table_cell.dart diff --git a/playground/frontend/lib/modules/output/components/output.dart b/playground/frontend/playground_components/lib/src/widgets/output/output.dart similarity index 64% rename from playground/frontend/lib/modules/output/components/output.dart rename to playground/frontend/playground_components/lib/src/widgets/output/output.dart index b99692749cbc..194b5d754937 100644 --- a/playground/frontend/lib/modules/output/components/output.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/output.dart @@ -17,39 +17,43 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/modules/output/components/output_area.dart'; -import 'package:playground/modules/output/components/output_header/output_placements.dart'; -import 'package:playground/modules/output/components/output_header/output_tabs.dart'; -import 'package:playground/modules/output/components/output_header/tab_header.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; + +import '../../controllers/playground_controller.dart'; +import '../tab_header.dart'; +import 'output_area.dart'; +import 'output_tabs.dart'; const kTabsCount = 2; -class Output extends StatefulWidget { - final bool isEmbedded; - final bool showGraph; +class OutputWidget extends StatefulWidget { + final PlaygroundController playgroundController; + final Widget? trailing; + final Axis graphDirection; - Output({ - required this.isEmbedded, - required PlaygroundState playgroundState, - }) : showGraph = playgroundState.graphAvailable, - super( + OutputWidget({ + required this.playgroundController, + required this.graphDirection, + this.trailing, + }) : super( key: ValueKey( - '${playgroundState.sdk}_${playgroundState.selectedExample?.path}', + '${playgroundController.sdk}_${playgroundController.selectedExample?.path}', ), ); @override - State createState() => _OutputState(); + State createState() => _OutputWidgetState(); } -class _OutputState extends State with SingleTickerProviderStateMixin { +class _OutputWidgetState extends State + with SingleTickerProviderStateMixin { late final TabController tabController; int selectedTab = 0; @override void initState() { - final tabsCount = widget.showGraph ? kTabsCount : kTabsCount - 1; + final tabsCount = widget.playgroundController.graphAvailable + ? kTabsCount + : kTabsCount - 1; tabController = TabController(vsync: this, length: tabsCount); tabController.addListener(_onTabChange); super.initState(); @@ -78,17 +82,18 @@ class _OutputState extends State with SingleTickerProviderStateMixin { TabHeader( tabController: tabController, tabsWidget: OutputTabs( + playgroundController: widget.playgroundController, tabController: tabController, - showGraph: widget.showGraph, ), ), - const OutputPlacements(), + if (widget.trailing != null) widget.trailing!, ], ), Expanded( child: OutputArea( + playgroundController: widget.playgroundController, tabController: tabController, - showGraph: widget.showGraph, + graphDirection: widget.graphDirection, ), ), ], diff --git a/playground/frontend/playground_components/lib/src/widgets/output/output_area.dart b/playground/frontend/playground_components/lib/src/widgets/output/output_area.dart new file mode 100644 index 000000000000..f1bf48b2c260 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/output/output_area.dart @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; +import 'package:playground_components/playground_components.dart'; + +import 'graph/graph.dart'; +import 'output_result.dart'; + +class OutputArea extends StatelessWidget { + final PlaygroundController playgroundController; + final TabController tabController; + final Axis graphDirection; + + const OutputArea({ + Key? key, + required this.playgroundController, + required this.tabController, + required this.graphDirection, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final sdk = playgroundController.sdk; + + return Container( + color: Theme.of(context).backgroundColor, + child: TabBarView( + controller: tabController, + physics: const NeverScrollableScrollPhysics(), + children: [ + OutputResult( + text: playgroundController.outputResult, + isSelected: tabController.index == 0, + ), + if (playgroundController.graphAvailable) + sdk == null + ? Container() + : GraphTab( + graph: playgroundController.result?.graph ?? '', + sdk: sdk, + direction: graphDirection, + ), + ], + ), + ); + } +} diff --git a/playground/frontend/lib/modules/output/components/output_result.dart b/playground/frontend/playground_components/lib/src/widgets/output/output_result.dart similarity index 86% rename from playground/frontend/lib/modules/output/components/output_result.dart rename to playground/frontend/playground_components/lib/src/widgets/output/output_result.dart index 63c90b2770a8..236a4856ff02 100644 --- a/playground/frontend/lib/modules/output/components/output_result.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/output_result.dart @@ -17,8 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/constants/fonts.dart'; -import 'package:playground/constants/sizes.dart'; + +import '../../constants/sizes.dart'; +import '../../theme/theme.dart'; class OutputResult extends StatefulWidget { final String text; @@ -48,6 +49,7 @@ class _OutputResultState extends State { @override Widget build(BuildContext context) { + final ext = Theme.of(context).extension()!; return SingleChildScrollView( controller: _scrollController, child: Scrollbar( @@ -55,8 +57,11 @@ class _OutputResultState extends State { trackVisibility: true, controller: _scrollController, child: Padding( - padding: const EdgeInsets.all(kXlSpacing), - child: SelectableText(widget.text, style: getCodeFontStyle()), + padding: const EdgeInsets.all(BeamSizes.size16), + child: SelectableText( + widget.text, + style: ext.codeRootStyle, + ), ), ), ); diff --git a/playground/frontend/lib/modules/output/components/output_header/output_tab.dart b/playground/frontend/playground_components/lib/src/widgets/output/output_tab.dart similarity index 78% rename from playground/frontend/lib/modules/output/components/output_header/output_tab.dart rename to playground/frontend/playground_components/lib/src/widgets/output/output_tab.dart index 4b6daed2a08f..326b1c72448f 100644 --- a/playground/frontend/lib/modules/output/components/output_header/output_tab.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/output_tab.dart @@ -18,23 +18,26 @@ import 'package:aligned_dialog/aligned_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/output/components/output_header/result_filter_popover.dart'; + +import '../../constants/sizes.dart'; +import '../../controllers/playground_controller.dart'; +import 'result_filter_popover.dart'; class OutputTab extends StatefulWidget { + final PlaygroundController playgroundController; final String name; final bool isSelected; final String value; final bool hasFilter; const OutputTab({ - Key? key, + super.key, + required this.playgroundController, required this.name, required this.isSelected, required this.value, this.hasFilter = false, - }) : super(key: key); + }); @override State createState() => _OutputTabState(); @@ -61,11 +64,13 @@ class _OutputTabState extends State { @override Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Tab( child: Wrap( direction: Axis.horizontal, alignment: WrapAlignment.center, - spacing: kMdSpacing, + spacing: BeamSizes.size8, children: [ Text(widget.name), widget.hasFilter @@ -73,7 +78,9 @@ class _OutputTabState extends State { onTap: () { showAlignedDialog( context: context, - builder: (dialogContext) => const ResultFilterPopover(), + builder: (dialogContext) => ResultFilterPopover( + playgroundController: widget.playgroundController, + ), followerAnchor: Alignment.topLeft, targetAnchor: Alignment.topLeft, barrierColor: Colors.transparent, @@ -81,17 +88,17 @@ class _OutputTabState extends State { }, child: Icon( Icons.filter_alt_outlined, - size: kIconSizeSm, - color: ThemeColors.of(context).primary, + size: BeamIconSizes.small, + color: themeData.primaryColor, ), ) : const SizedBox(), if (hasNewContent) Container( - width: kIconSizeXs, - height: kIconSizeXs, + width: BeamIconSizes.xs, + height: BeamIconSizes.xs, decoration: BoxDecoration( - color: ThemeColors.of(context).primary, + color: themeData.primaryColor, shape: BoxShape.circle, ), ), diff --git a/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart b/playground/frontend/playground_components/lib/src/widgets/output/output_tabs.dart similarity index 51% rename from playground/frontend/lib/modules/output/components/output_header/output_tabs.dart rename to playground/frontend/playground_components/lib/src/widgets/output/output_tabs.dart index 6107769a5298..80839296fbfa 100644 --- a/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart +++ b/playground/frontend/playground_components/lib/src/widgets/output/output_tabs.dart @@ -16,46 +16,45 @@ * limitations under the License. */ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/modules/output/components/output_header/output_tab.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:provider/provider.dart'; + +import '../../controllers/playground_controller.dart'; +import 'output_tab.dart'; class OutputTabs extends StatelessWidget { + final PlaygroundController playgroundController; final TabController tabController; - final bool showGraph; const OutputTabs({ - Key? key, + super.key, + required this.playgroundController, required this.tabController, - required this.showGraph, - }) : super(key: key); + }); @override Widget build(BuildContext context) { - AppLocalizations appLocale = AppLocalizations.of(context)!; - return Consumer(builder: (context, state, child) { - return SizedBox( - width: 300, - child: TabBar( - controller: tabController, - tabs: [ + return SizedBox( + width: 300, + child: TabBar( + controller: tabController, + tabs: [ + OutputTab( + playgroundController: playgroundController, + name: 'widgets.output.result'.tr(), + isSelected: tabController.index == 0, + value: playgroundController.outputResult, + hasFilter: true, + ), + if (playgroundController.graphAvailable) OutputTab( - name: appLocale.result, - isSelected: tabController.index == 0, - value: state.outputResult, - hasFilter: true, + playgroundController: playgroundController, + name: 'widgets.output.graph'.tr(), + isSelected: tabController.index == 2, + value: playgroundController.result?.graph ?? '', ), - if (showGraph) - OutputTab( - name: appLocale.graph, - isSelected: tabController.index == 2, - value: state.result?.graph ?? '', - ), - ], - ), - ); - }); + ], + ), + ); } } diff --git a/playground/frontend/playground_components/lib/src/widgets/output/result_filter_bubble.dart b/playground/frontend/playground_components/lib/src/widgets/output/result_filter_bubble.dart new file mode 100644 index 000000000000..a18ddb01d81a --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/output/result_filter_bubble.dart @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; + +import '../../controllers/playground_controller.dart'; +import '../../models/outputs.dart'; +import '../bubble.dart'; + +class ResultFilterBubble extends StatelessWidget { + final PlaygroundController playgroundController; + final OutputType type; + final String name; + + const ResultFilterBubble({ + super.key, + required this.playgroundController, + required this.type, + required this.name, + }); + + @override + Widget build(BuildContext context) { + final isSelected = type == playgroundController.selectedOutputFilterType; + + return BubbleWidget( + isSelected: isSelected, + onTap: () { + if (!isSelected) { + playgroundController.setSelectedOutputFilterType(type); + playgroundController.filterOutput(type); + } + }, + title: name, + ); + } +} diff --git a/playground/frontend/playground_components/lib/src/widgets/output/result_filter_popover.dart b/playground/frontend/playground_components/lib/src/widgets/output/result_filter_popover.dart new file mode 100644 index 000000000000..a74c67e2fa17 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/output/result_filter_popover.dart @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; + +import 'package:playground_components/playground_components.dart'; + +import 'result_filter_bubble.dart'; + +const kPopoverWidth = 240.0; +const kPopoverPadding = 50.0; + +class ResultFilterPopover extends StatelessWidget { + final PlaygroundController playgroundController; + + const ResultFilterPopover({ + required this.playgroundController, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: kPopoverPadding), + child: SizedBox( + width: kPopoverWidth, + child: Card( + child: Padding( + padding: const EdgeInsets.all(BeamSizes.size8), + child: Wrap( + runSpacing: BeamSizes.size8, + children: [ + const Text('widgets.output.filterTitle').tr(), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: BeamSizes.size4, + vertical: BeamSizes.size4, + ), + child: AnimatedBuilder( + animation: playgroundController, + builder: (context, child) => Row( + children: [ + ResultFilterBubble( + playgroundController: playgroundController, + type: OutputType.all, + name: 'widgets.output.filter.all'.tr(), + ), + ResultFilterBubble( + playgroundController: playgroundController, + type: OutputType.log, + name: 'widgets.output.filter.log'.tr(), + ), + ResultFilterBubble( + playgroundController: playgroundController, + type: OutputType.output, + name: 'widgets.output.filter.output'.tr(), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/playground/frontend/playground_components/lib/src/widgets/reset_button.dart b/playground/frontend/playground_components/lib/src/widgets/reset_button.dart new file mode 100644 index 000000000000..079587dddedb --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/reset_button.dart @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../constants/playground_components.dart'; +import '../controllers/playground_controller.dart'; +import '../generated/assets.gen.dart'; +import '../theme/theme.dart'; +import 'header_icon_button.dart'; +import 'shortcut_tooltip.dart'; + +class ResetButton extends StatelessWidget { + final PlaygroundController playgroundController; + final VoidCallback? beforeReset; + + const ResetButton({ + required this.playgroundController, + this.beforeReset, + }); + + @override + Widget build(BuildContext context) { + return ShortcutTooltip( + shortcut: playgroundController.resetShortcut, + child: HeaderIconButton( + icon: SvgPicture.asset( + Assets.buttons.reset, + color: Theme.of(context).extension()?.iconColor, + package: PlaygroundComponents.packageName, + ), + label: 'widgets.resetButton.label'.tr(), + onPressed: () { + beforeReset?.call(); + playgroundController.reset(); + }, + ), + ); + } +} diff --git a/playground/frontend/lib/modules/editor/components/run_button.dart b/playground/frontend/playground_components/lib/src/widgets/run_button.dart similarity index 69% rename from playground/frontend/lib/modules/editor/components/run_button.dart rename to playground/frontend/playground_components/lib/src/widgets/run_button.dart index d31e36d21351..9aabaeb6b1f4 100644 --- a/playground/frontend/lib/modules/editor/components/run_button.dart +++ b/playground/frontend/playground_components/lib/src/widgets/run_button.dart @@ -16,55 +16,58 @@ * limitations under the License. */ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/sizes.dart'; -import 'package:playground/modules/shortcuts/components/shortcut_tooltip.dart'; -import 'package:playground/modules/shortcuts/constants/global_shortcuts.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; -import 'package:provider/provider.dart'; + +import '../constants/sizes.dart'; +import '../controllers/playground_controller.dart'; +import '../theme/theme.dart'; +import 'shortcut_tooltip.dart'; const kMsToSec = 1000; const kSecondsFractions = 1; +const _width = 150.0; + class RunButton extends StatelessWidget { + final PlaygroundController playgroundController; final bool isRunning; final VoidCallback runCode; final VoidCallback cancelRun; final bool disabled; const RunButton({ - Key? key, + super.key, + required this.playgroundController, required this.isRunning, required this.runCode, required this.cancelRun, this.disabled = false, - }) : super(key: key); + }); @override Widget build(BuildContext context) { return SizedBox( - width: kRunButtonWidth, - height: kButtonHeight, + width: _width, + height: BeamSizes.buttonHeight, child: ShortcutTooltip( - shortcut: kRunShortcut, + shortcut: playgroundController.runShortcut, child: ElevatedButton.icon( icon: isRunning ? SizedBox( - width: kIconSizeSm, - height: kIconSizeSm, + width: BeamIconSizes.small, + height: BeamIconSizes.small, child: CircularProgressIndicator( - color: ThemeColors.of(context).primaryBackgroundTextColor, + color: Theme.of(context).extension()!.primaryBackgroundTextColor, ), ) : const Icon(Icons.play_arrow), label: StreamBuilder( - stream: Provider.of(context).executionTime, + stream: playgroundController.executionTime, builder: (context, AsyncSnapshot state) { final seconds = (state.data ?? 0) / kMsToSec; - final runText = AppLocalizations.of(context)!.run; - final cancelText = AppLocalizations.of(context)!.cancel; + final runText = 'widgets.runOrCancelButton.titles.run'.tr(); + final cancelText = 'widgets.runOrCancelButton.titles.cancel'.tr(); final buttonText = isRunning ? cancelText : runText; if (seconds > 0) { return Text( diff --git a/playground/frontend/playground_components/lib/src/widgets/run_or_cancel_button.dart b/playground/frontend/playground_components/lib/src/widgets/run_or_cancel_button.dart new file mode 100644 index 000000000000..7bbd204fcb2c --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/run_or_cancel_button.dart @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/widgets.dart'; + +import '../controllers/playground_controller.dart'; +import '../notifications/notification.dart'; +import 'run_button.dart'; + +class RunOrCancelButton extends StatelessWidget { + final VoidCallback? beforeCancel; + final VoidCallback? onComplete; + final VoidCallback? beforeRun; + final PlaygroundController playgroundController; + + const RunOrCancelButton({ + required this.playgroundController, + this.beforeCancel, + this.onComplete, + this.beforeRun, + }); + + @override + Widget build(BuildContext context) { + return RunButton( + playgroundController: playgroundController, + disabled: playgroundController.selectedExample?.isMultiFile ?? false, + isRunning: playgroundController.isCodeRunning, + cancelRun: () { + beforeCancel?.call(); + playgroundController.cancelRun().catchError( + (_) => NotificationManager.showError( + context, + 'widgets.runOrCancelButton.notificationTitles.runCode'.tr(), + 'widgets.runOrCancelButton.notificationTitles.cancelExecution'.tr(), + ), + ); + }, + runCode: () { + beforeRun?.call(); + playgroundController.runCode( + onFinish: onComplete, + ); + }, + ); + } +} diff --git a/playground/frontend/lib/modules/shortcuts/components/shortcut_tooltip.dart b/playground/frontend/playground_components/lib/src/widgets/shortcut_tooltip.dart similarity index 81% rename from playground/frontend/lib/modules/shortcuts/components/shortcut_tooltip.dart rename to playground/frontend/playground_components/lib/src/widgets/shortcut_tooltip.dart index 94f14677c14b..80e75b9e0788 100644 --- a/playground/frontend/lib/modules/shortcuts/components/shortcut_tooltip.dart +++ b/playground/frontend/playground_components/lib/src/widgets/shortcut_tooltip.dart @@ -17,24 +17,24 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/modules/shortcuts/models/shortcut.dart'; -import 'package:playground/modules/shortcuts/utils/shortcuts_display_name.dart'; + +import '../models/shortcut.dart'; class ShortcutTooltip extends StatelessWidget { - final Shortcut shortcut; + final BeamShortcut shortcut; final Widget child; const ShortcutTooltip({ - Key? key, + super.key, required this.shortcut, required this.child, - }) : super(key: key); + }); @override Widget build(BuildContext context) { return Tooltip( excludeFromSemantics: true, - message: getShortcutDisplayName(shortcut), + message: shortcut.title, child: child, ); } diff --git a/playground/frontend/playground_components/lib/src/widgets/snippet_editor.dart b/playground/frontend/playground_components/lib/src/widgets/snippet_editor.dart new file mode 100644 index 000000000000..fe7ecc4e6037 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/snippet_editor.dart @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/widgets.dart'; + +import '../controllers/snippet_editing_controller.dart'; +import 'editor_textarea.dart'; + +class SnippetEditor extends StatelessWidget { + final SnippetEditingController controller; + final bool isEditable; + final bool goToContextLine; + + const SnippetEditor({ + required this.controller, + required this.isEditable, + required this.goToContextLine, + }); + + @override + Widget build(BuildContext context) { + return EditorTextArea( + codeController: controller.codeController, + sdk: controller.sdk, + enabled: !(controller.selectedExample?.isMultiFile ?? false), + example: controller.selectedExample, + isEditable: isEditable, + goToContextLine: goToContextLine, + ); + } +} diff --git a/playground/frontend/lib/components/split_view/split_view.dart b/playground/frontend/playground_components/lib/src/widgets/split_view.dart similarity index 79% rename from playground/frontend/lib/components/split_view/split_view.dart rename to playground/frontend/playground_components/lib/src/widgets/split_view.dart index 06403d6759a7..937685618dd3 100644 --- a/playground/frontend/lib/components/split_view/split_view.dart +++ b/playground/frontend/playground_components/lib/src/widgets/split_view.dart @@ -17,14 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/assets.dart'; -enum SplitViewDirection { - vertical, - horizontal, -} +import '../constants/sizes.dart'; +import 'drag_handle.dart'; const minRatio = 0.3; const maxRatio = 0.7; @@ -33,18 +28,16 @@ const defaultRatio = 0.5; class SplitView extends StatefulWidget { final Widget first; final Widget second; - final double dividerSize; - final SplitViewDirection direction; + final Axis direction; final double ratio; const SplitView({ - Key? key, + super.key, required this.first, required this.second, - required this.dividerSize, required this.direction, this.ratio = defaultRatio, - }) : super(key: key); + }); @override State createState() => _SplitViewState(); @@ -59,9 +52,9 @@ class _SplitViewState extends State { get _sizeSecond => (1 - _ratio) * _maxSize; - get _isHorizontal => widget.direction == SplitViewDirection.horizontal; + get _isHorizontal => widget.direction == Axis.horizontal; - get _isVertical => widget.direction == SplitViewDirection.vertical; + get _isVertical => widget.direction == Axis.vertical; @override void initState() { @@ -79,7 +72,8 @@ class _SplitViewState extends State { }); } - _buildHorizontalLayout(BuildContext context, BoxConstraints constraints) { + Widget _buildHorizontalLayout( + BuildContext context, BoxConstraints constraints) { return SizedBox( width: constraints.maxWidth, child: Row( @@ -126,14 +120,13 @@ class _SplitViewState extends State { child: GestureDetector( behavior: HitTestBehavior.translucent, child: Container( - width: _isHorizontal ? widget.dividerSize : double.infinity, - height: _isVertical ? widget.dividerSize : double.infinity, - color: ThemeColors.of(context).divider, - child: Center( - child: SvgPicture.asset(_isHorizontal - ? kDragHorizontalIconAsset - : kDragVerticalIconAsset), - )), + width: _isHorizontal ? BeamSizes.splitViewSeparator : double.infinity, + height: _isVertical ? BeamSizes.splitViewSeparator : double.infinity, + color: Theme.of(context).dividerColor, + child: Center( + child: DragHandle(direction: widget.direction), + ), + ), onPanUpdate: (DragUpdateDetails details) { setState(() { _updateRatio(details); @@ -171,7 +164,7 @@ class _SplitViewState extends State { void _calculateMaxSize(double maxSize) { if (_maxSize != maxSize) { - _maxSize = maxSize - widget.dividerSize; + _maxSize = maxSize - BeamSizes.splitViewSeparator; } } } diff --git a/playground/frontend/lib/modules/output/components/output_header/tab_header.dart b/playground/frontend/playground_components/lib/src/widgets/tab_header.dart similarity index 91% rename from playground/frontend/lib/modules/output/components/output_header/tab_header.dart rename to playground/frontend/playground_components/lib/src/widgets/tab_header.dart index dbf60b30f8a4..714f025814e0 100644 --- a/playground/frontend/lib/modules/output/components/output_header/tab_header.dart +++ b/playground/frontend/playground_components/lib/src/widgets/tab_header.dart @@ -17,7 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:playground/constants/sizes.dart'; + +import '../constants/sizes.dart'; const kHeaderHeight = 50.0; @@ -37,8 +38,7 @@ class TabHeader extends StatelessWidget { height: 50, child: Padding( padding: const EdgeInsets.symmetric( - horizontal: kXlSpacing, - vertical: kZeroSpacing, + horizontal: BeamSizes.size16, ), child: tabsWidget, ), diff --git a/playground/frontend/playground_components/lib/src/widgets/toggle_theme_button.dart b/playground/frontend/playground_components/lib/src/widgets/toggle_theme_button.dart index abd0b82edd56..927bda590ebe 100644 --- a/playground/frontend/playground_components/lib/src/widgets/toggle_theme_button.dart +++ b/playground/frontend/playground_components/lib/src/widgets/toggle_theme_button.dart @@ -21,6 +21,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; +import '../constants/playground_components.dart'; import '../generated/assets.gen.dart'; import '../theme/switch_notifier.dart'; @@ -35,7 +36,10 @@ class ToggleThemeButton extends StatelessWidget { notifier.isDarkMode ? 'ui.lightMode'.tr() : 'ui.darkMode'.tr(); return TextButton.icon( - icon: SvgPicture.asset(Assets.svg.themeMode), + icon: SvgPicture.asset( + Assets.buttons.themeMode, + package: PlaygroundComponents.packageName, + ), label: Text(text), onPressed: () { notifier.toggleTheme(); diff --git a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart b/playground/frontend/playground_components/lib/src/widgets/toggle_theme_icon_button.dart similarity index 73% rename from playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart rename to playground/frontend/playground_components/lib/src/widgets/toggle_theme_icon_button.dart index f82270826fea..982aa39f80e6 100644 --- a/playground/frontend/lib/components/toggle_theme_button/toggle_theme_icon_button.dart +++ b/playground/frontend/playground_components/lib/src/widgets/toggle_theme_icon_button.dart @@ -18,21 +18,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:playground/config/theme.dart'; -import 'package:playground/constants/assets.dart'; -import 'package:playground/constants/sizes.dart'; import 'package:provider/provider.dart'; +import '../constants/playground_components.dart'; +import '../constants/sizes.dart'; +import '../generated/assets.gen.dart'; +import '../theme/switch_notifier.dart'; + class ToggleThemeIconButton extends StatelessWidget { - const ToggleThemeIconButton({Key? key}) : super(key: key); + const ToggleThemeIconButton({super.key}); @override Widget build(BuildContext context) { return Consumer(builder: (context, notifier, child) { return IconButton( - iconSize: kIconSizeLg, - splashRadius: kIconButtonSplashRadius, - icon: SvgPicture.asset(kThemeIconAsset), + iconSize: BeamIconSizes.large, + splashRadius: BeamIconSizes.largeSplashRadius, + icon: SvgPicture.asset( + Assets.buttons.themeMode, + package: PlaygroundComponents.packageName, + ), onPressed: notifier.toggleTheme, ); }); diff --git a/playground/frontend/playground_components/pubspec.yaml b/playground/frontend/playground_components/pubspec.yaml index d489229cb822..1321bee934cc 100644 --- a/playground/frontend/playground_components/pubspec.yaml +++ b/playground/frontend/playground_components/pubspec.yaml @@ -24,25 +24,40 @@ environment: flutter: '>=1.17.0' dependencies: + aligned_dialog: ^0.0.6 + code_text_field: + git: + url: https://github.com/BertrandBev/code_field.git + ref: 9e2c9fe52a69481f038f4b6609e8a0a776429437 + collection: ^1.16.0 easy_localization: ^3.0.1 + easy_localization_ext: ^0.1.1 easy_localization_loader: ^1.0.0 + equatable: ^2.0.5 flutter: { sdk: flutter } flutter_svg: ^1.0.3 google_fonts: ^3.0.1 + grpc: ^3.0.2 + highlight: ^0.7.0 + meta: ^1.7.0 provider: ^6.0.3 + protobuf: ^2.1.0 shared_preferences: ^2.0.15 dev_dependencies: build_runner: ^2.2.0 flutter_gen_runner: ^4.3.0 flutter_test: { sdk: flutter } + mockito: 5.2.0 total_lints: ^2.17.4 flutter: uses-material-design: true assets: - - assets/svg/ + - assets/buttons/ + - assets/notification_icons/ - assets/png/ + - assets/svg/ - assets/translations/en.yaml flutter_gen: diff --git a/playground/frontend/test/pages/playground/states/examples_state_test.dart b/playground/frontend/playground_components/test/src/cache/example_cache_test.dart similarity index 52% rename from playground/frontend/test/pages/playground/states/examples_state_test.dart rename to playground/frontend/playground_components/test/src/cache/example_cache_test.dart index 169dce298a2e..4b461ba69e9c 100644 --- a/playground/frontend/test/pages/playground/states/examples_state_test.dart +++ b/playground/frontend/playground_components/test/src/cache/example_cache_test.dart @@ -16,35 +16,37 @@ * limitations under the License. */ +import 'dart:collection'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; - -import 'mocks/categories_mock.dart'; -import 'mocks/example_mock.dart'; -import 'mocks/example_repository_mock.dart'; -import 'mocks/example_repository_mock.mocks.dart'; -import 'mocks/request_mock.dart'; - -final kDefaultExamplesMapMock = { - SDK.java: exampleWithAllAdditionsMock, - SDK.go: exampleWithAllAdditionsMock, - SDK.python: exampleWithAllAdditionsMock, - SDK.scio: exampleWithAllAdditionsMock, -}; +import 'package:playground_components/src/cache/example_cache.dart'; +import 'package:playground_components/src/models/sdk.dart'; + +import '../common/categories.dart'; +import '../common/example_repository_mock.dart'; +import '../common/example_repository_mock.mocks.dart'; +import '../common/examples.dart'; +import '../common/requests.dart'; + +final kDefaultExamplesMapMock = UnmodifiableMapView({ + Sdk.java: exampleWithAllAdditionsMock, + Sdk.go: exampleWithAllAdditionsMock, + Sdk.python: exampleWithAllAdditionsMock, + Sdk.scio: exampleWithAllAdditionsMock, +}); void main() { - late ExampleState state; + late ExampleCache state; late MockExampleRepository mockRepo; setUp(() { mockRepo = getMockExampleRepository(); - state = ExampleState(mockRepo); + state = ExampleCache(exampleRepository: mockRepo, hasCatalog: true); }); - test('Initial value of defaultExamplesMap should be an empty map', () { - expect(state.defaultExamplesMap, {}); + test('Initial value of defaultExamplesBySdk should be an empty map', () { + expect(state.defaultExamplesBySdk, {}); }); test('Initial value of isSelectorOpened should be false', () { @@ -52,20 +54,20 @@ void main() { }); test( - 'Example state init should initiate loading of sdkCategories from server', + 'Example state init should initiate loading of categoryListsBySdk from server', () async { - when(mockRepo.getListOfExamples(kGetListOfExamplesRequestMock)) - .thenAnswer((_) async => kGetListOfExamplesResponseMock.categories); + when(mockRepo.getListOfExamples(kGetPrecompiledObjectsRequest)) + .thenAnswer((_) async => kGetPrecompiledObjectsResponse.categories); await state.init(); - expect(state.sdkCategories, sdkCategoriesFromServerMock); + expect(state.categoryListsBySdk, sdkCategoriesFromServerMock); }, ); test( - 'Example state should notify all listeners about sdkCategories is set', + 'Example state should notify all listeners about categoryListsBySdk is set', () { state.addListener(() { - expect(state.sdkCategories, sdkCategoriesFromServerMock); + expect(state.categoryListsBySdk, sdkCategoriesFromServerMock); }); state.setSdkCategories(sdkCategoriesFromServerMock); }, @@ -85,21 +87,24 @@ void main() { 'Example state getCategories should get the categories list for each SDK', () { state.setSdkCategories(sdkCategoriesFromServerMock); - expect(state.getCategories(SDK.java), categoriesMock); - expect(state.getCategories(SDK.go), categoriesMock); - expect(state.getCategories(SDK.python), categoriesMock); - expect(state.getCategories(SDK.scio), categoriesMock); + expect(state.getCategories(Sdk.java), categoriesMock); + expect(state.getCategories(Sdk.go), categoriesMock); + expect(state.getCategories(Sdk.python), categoriesMock); + expect(state.getCategories(Sdk.scio), categoriesMock); }, ); test( 'Example state getExampleOutput should return output for example', () async { - when(mockRepo.getExampleOutput(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock.output); + when(mockRepo.getExampleOutput(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse.output); expect( - await state.getExampleOutput('', SDK.java), - kOutputResponseMock.output, + await state.getExampleOutput( + kRequestForExampleInfo.path, + kRequestForExampleInfo.sdk, + ), + kOutputResponse.output, ); }, ); @@ -107,11 +112,14 @@ void main() { test( 'Example state getExampleSource should return source code for example', () async { - when(mockRepo.getExampleSource(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock.output); + when(mockRepo.getExampleSource(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse.output); expect( - await state.getExampleSource('', SDK.java), - kOutputResponseMock.output, + await state.getExampleSource( + kRequestForExampleInfo.path, + kRequestForExampleInfo.sdk, + ), + kOutputResponse.output, ); }, ); @@ -119,11 +127,14 @@ void main() { test( 'Example state getExampleLogs should return logs for example', () async { - when(mockRepo.getExampleLogs(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock.output); + when(mockRepo.getExampleLogs(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse.output); expect( - await state.getExampleLogs('', SDK.java), - kOutputResponseMock.output, + await state.getExampleLogs( + kRequestForExampleInfo.path, + kRequestForExampleInfo.sdk, + ), + kOutputResponse.output, ); }, ); @@ -131,18 +142,21 @@ void main() { test( 'Example state getExampleGraph should return output for example', () async { - when(mockRepo.getExampleGraph(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock.output); + when(mockRepo.getExampleGraph(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse.output); expect( - await state.getExampleGraph('', SDK.java), - kOutputResponseMock.output, + await state.getExampleGraph( + kRequestForExampleInfo.path, + kRequestForExampleInfo.sdk, + ), + kOutputResponse.output, ); }, ); group('loadExampleInfo tests', () { test( - 'If example info is fetched (source is not empty),' + 'If example info is fetched (source is not empty), ' 'then loadExampleInfo should return example immediately', () async { expect( @@ -165,31 +179,31 @@ void main() { group('loadDefaultExamples tests', () { test( - 'If defaultExamplesMap is not empty, then loadDefaultExamples should not change it', + 'If defaultExamplesBySdk is not empty, then loadDefaultExamples should not change it', () async { - state.defaultExamplesMap = kDefaultExamplesMapMock; + state.defaultExamplesBySdk.addAll(kDefaultExamplesMapMock); await state.loadDefaultExamples(); - expect(state.defaultExamplesMap, kDefaultExamplesMapMock); + expect(state.defaultExamplesBySdk, kDefaultExamplesMapMock); }, ); test( - 'Example state loadDefaultExamples should load default example' + 'Example state loadDefaultExamples should load default example ' 'with all additions for every Sdk', () async { // stubs when(mockRepo.getExampleOutput(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(mockRepo.getExampleSource(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(mockRepo.getExampleLogs(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(mockRepo.getExampleGraph(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); // test assertion await state.loadDefaultExamples(); expect( - state.defaultExamplesMap, + state.defaultExamplesBySdk, kDefaultExamplesMapMock, ); }, diff --git a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart b/playground/frontend/playground_components/test/src/common/categories.dart similarity index 51% rename from playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart rename to playground/frontend/playground_components/test/src/common/categories.dart index aec65ba2f246..39c0f4c38bcb 100644 --- a/playground/frontend/lib/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart +++ b/playground/frontend/playground_components/test/src/common/categories.dart @@ -16,29 +16,31 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_origin.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'dart:collection'; -class EmptyExampleLoadingDescriptor extends ExampleLoadingDescriptor { - final SDK sdk; +import 'package:playground_components/src/models/category_with_examples.dart'; +import 'package:playground_components/src/models/sdk.dart'; - const EmptyExampleLoadingDescriptor({ - required this.sdk, - }); +import 'examples.dart'; - @override - ExampleOrigin get origin => ExampleOrigin.empty; +final categoriesMock = [ + CategoryWithExamples(title: 'Sorted', examples: [exampleMock1]), + CategoryWithExamples(title: 'Unsorted', examples: [exampleMock2]), +]; - @override - int get hashCode => sdk.hashCode; +final sortedCategories = [ + CategoryWithExamples(title: 'Sorted', examples: [exampleMock1]), +]; - @override - bool operator ==(Object other) { - return other is EmptyExampleLoadingDescriptor && sdk == other.sdk; - } +const unsortedExamples = [exampleMock1, exampleMock2]; - // Only ContentExampleLoadingDescriptor is serialized now. - @override - Map toJson() => throw UnimplementedError(); -} +const examplesSortedByTypeMock = [exampleMock2]; + +const examplesSortedByNameMock = [exampleMock1]; + +final sdkCategoriesFromServerMock = UnmodifiableMapView({ + Sdk.java: categoriesMock, + Sdk.python: categoriesMock, + Sdk.go: categoriesMock, + Sdk.scio: categoriesMock, +}); diff --git a/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart b/playground/frontend/playground_components/test/src/common/example_repository_mock.dart similarity index 82% rename from playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart rename to playground/frontend/playground_components/test/src/common/example_repository_mock.dart index e44c5f03b4e6..108d9b1195a1 100644 --- a/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.dart +++ b/playground/frontend/playground_components/test/src/common/example_repository_mock.dart @@ -18,11 +18,11 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:playground/modules/examples/repositories/example_repository.dart'; +import 'package:playground_components/src/repositories/example_repository.dart'; import 'example_repository_mock.mocks.dart'; -import 'example_mock.dart'; -import 'request_mock.dart'; +import 'examples.dart'; +import 'requests.dart'; @GenerateMocks([ExampleRepository]) MockExampleRepository getMockExampleRepository() { @@ -39,13 +39,13 @@ MockExampleRepository getMockExampleRepository() { .thenAnswer((_) async => exampleWithoutSourceMock); when(m.getExampleOutput(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(m.getExampleSource(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(m.getExampleLogs(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); when(m.getExampleGraph(kRequestForExampleInfo)) - .thenAnswer((_) async => kOutputResponseMock.output); + .thenAnswer((_) async => kOutputResponse.output); return m; } diff --git a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart b/playground/frontend/playground_components/test/src/common/examples.dart similarity index 72% rename from playground/frontend/test/pages/playground/states/mocks/example_mock.dart rename to playground/frontend/playground_components/test/src/common/examples.dart index f0833e8afb48..a522668827de 100644 --- a/playground/frontend/test/pages/playground/states/mocks/example_mock.dart +++ b/playground/frontend/playground_components/test/src/common/examples.dart @@ -16,37 +16,41 @@ * limitations under the License. */ -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/src/models/example.dart'; +import 'package:playground_components/src/models/example_base.dart'; +import 'package:playground_components/src/models/sdk.dart'; -final ExampleModel exampleMock1 = ExampleModel( - sdk: SDK.python, +const exampleMock1 = Example( + sdk: Sdk.python, source: 'ex1', name: 'Example', type: ExampleType.example, description: 'description', path: 'SDK_PYTHON/Category/Name', + pipelineOptions: '', ); -final ExampleModel exampleMock2 = ExampleModel( - sdk: SDK.python, +const exampleMock2 = Example( + sdk: Sdk.python, source: 'ex2', name: 'Kata', type: ExampleType.kata, description: 'description', path: 'SDK_PYTHON/Category/Name', + pipelineOptions: '', ); -final ExampleModel exampleWithoutSourceMock = ExampleModel( - sdk: SDK.python, +const exampleWithoutSourceMock = ExampleBase( + sdk: Sdk.python, name: 'Test example', type: ExampleType.example, description: 'description', path: 'SDK_PYTHON/Category/Name', + pipelineOptions: '', ); -final ExampleModel exampleWithAllAdditionsMock = ExampleModel( - sdk: SDK.python, +const exampleWithAllAdditionsMock = Example( + sdk: Sdk.python, name: 'Test example', type: ExampleType.example, description: 'description', @@ -55,13 +59,15 @@ final ExampleModel exampleWithAllAdditionsMock = ExampleModel( outputs: 'test outputs', logs: 'test outputs', graph: 'test outputs', + pipelineOptions: '', ); -final ExampleModel exampleMockGo = ExampleModel( - sdk: SDK.go, +const exampleMockGo = Example( + sdk: Sdk.go, source: 'ex1', name: 'Example', type: ExampleType.example, description: 'description', path: 'SDK_GO/Category/Name', + pipelineOptions: '', ); diff --git a/playground/frontend/playground_components/test/src/common/requests.dart b/playground/frontend/playground_components/test/src/common/requests.dart new file mode 100644 index 000000000000..84d015884df1 --- /dev/null +++ b/playground/frontend/playground_components/test/src/common/requests.dart @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:playground_components/src/models/sdk.dart'; +import 'package:playground_components/src/repositories/models/get_default_precompiled_object_request.dart'; +import 'package:playground_components/src/repositories/models/get_precompiled_object_code_response.dart'; +import 'package:playground_components/src/repositories/models/get_precompiled_object_request.dart'; +import 'package:playground_components/src/repositories/models/get_precompiled_object_response.dart'; +import 'package:playground_components/src/repositories/models/get_precompiled_objects_request.dart'; +import 'package:playground_components/src/repositories/models/get_precompiled_objects_response.dart'; +import 'package:playground_components/src/repositories/models/output_response.dart'; + +import 'categories.dart'; +import 'examples.dart'; + +const kGetPrecompiledObjectsRequest = GetPrecompiledObjectsRequest( + sdk: null, + category: null, +); +final kGetPrecompiledObjectsResponse = GetPrecompiledObjectsResponse( + categories: sdkCategoriesFromServerMock, +); + +const kGetDefaultPrecompiledObjectRequest = GetDefaultPrecompiledObjectRequest( + sdk: Sdk.java, +); +const kGetDefaultPrecompiledObjectResponse = GetPrecompiledObjectResponse( + example: exampleMock1, +); + +const kGetPrecompiledObjectCodeResponse = GetPrecompiledObjectCodeResponse( + code: 'test source', +); +const kOutputResponse = OutputResponse(output: 'test outputs'); + +const kRequestForExampleInfo = GetPrecompiledObjectRequest( + path: 'SDK_PYTHON/Category/Name', + sdk: Sdk.python, +); +const kRequestDefaultExampleForJava = GetDefaultPrecompiledObjectRequest( + sdk: Sdk.java, +); +const kRequestDefaultExampleForGo = GetDefaultPrecompiledObjectRequest( + sdk: Sdk.go, +); +const kRequestDefaultExampleForPython = GetDefaultPrecompiledObjectRequest( + sdk: Sdk.python, +); +const kRequestDefaultExampleForScio = GetDefaultPrecompiledObjectRequest( + sdk: Sdk.scio, +); diff --git a/playground/frontend/test/pages/playground/states/playground_state_test.dart b/playground/frontend/playground_components/test/src/controllers/playground_controller_test.dart similarity index 79% rename from playground/frontend/test/pages/playground/states/playground_state_test.dart rename to playground/frontend/playground_components/test/src/controllers/playground_controller_test.dart index 411a935bc2a2..506102cce5cc 100644 --- a/playground/frontend/test/pages/playground/states/playground_state_test.dart +++ b/playground/frontend/playground_components/test/src/controllers/playground_controller_test.dart @@ -19,32 +19,32 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/src/cache/example_cache.dart'; +import 'package:playground_components/src/controllers/example_loaders/examples_loader.dart'; +import 'package:playground_components/src/controllers/playground_controller.dart'; +import 'package:playground_components/src/models/sdk.dart'; -import 'mocks/example_mock.dart'; -import 'playground_state_test.mocks.dart'; +import '../common/examples.dart'; +import 'playground_controller_test.mocks.dart'; -@GenerateMocks([ExamplesLoader, ExampleState]) +@GenerateMocks([ExamplesLoader, ExampleCache]) void main() { - late PlaygroundState state; + late PlaygroundController state; final mockExamplesLoader = MockExamplesLoader(); when(mockExamplesLoader.load(any)).thenAnswer((_) async => 1); setUp(() { - state = PlaygroundState( + state = PlaygroundController( examplesLoader: MockExamplesLoader(), - exampleState: MockExampleState(), + exampleCache: MockExampleCache(), ); }); test('Initial value of SDK field should be null', () { expect(state.sdk, null); - state.setSdk(SDK.go); - expect(state.sdk, SDK.go); + state.setSdk(Sdk.go); + expect(state.sdk, Sdk.go); }); test('Initial value of examplesTitle should be equal to kTitle', () { @@ -57,13 +57,13 @@ void main() { test('Initial value of pipelineOptions should be empty string', () { expect(state.pipelineOptions, null); - state.setSdk(SDK.go); + state.setSdk(Sdk.go); expect(state.pipelineOptions, ''); }); test('Initial value of source should be empty string', () { expect(state.source, null); - state.setSdk(SDK.go); + state.setSdk(Sdk.go); expect(state.source, ''); }); @@ -73,7 +73,10 @@ void main() { () { state.setExample(exampleMock1, setCurrentSdk: true); expect(state.isExampleChanged, false); - state.selectedExample!.setSource('test'); + // 'test' in this line hits a bug fixed here: + // https://github.com/akvelon/flutter-code-editor/commit/c74ce566bf873dc76a5269ce6fe7b02df9c148e0 + // TODO(alexeyinkin): revert from 'test1' to 'test' when Akvelon's editor is integrated. + state.setSource('test1'); expect(state.isExampleChanged, true); }, ); @@ -101,7 +104,7 @@ void main() { 'Playground state setExample should update source and example and notify all listeners', () { state.addListener(() { - expect(state.sdk, SDK.go); + expect(state.sdk, Sdk.go); expect(state.source, exampleMockGo.source); expect(state.selectedExample, exampleMockGo); }); @@ -111,9 +114,9 @@ void main() { test('Playground state should notify all listeners about sdk change', () { state.addListener(() { - expect(state.sdk, SDK.go); + expect(state.sdk, Sdk.go); }); - state.setSdk(SDK.go); + state.setSdk(Sdk.go); }); test( @@ -138,7 +141,7 @@ void main() { test( 'Playground state should notify all listeners about pipeline options change', () { - state.setSdk(SDK.go); + state.setSdk(Sdk.go); state.addListener(() { expect(state.pipelineOptions, 'test options'); }); diff --git a/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.dart b/playground/frontend/playground_components/test/src/repositories/code_repository_test.dart similarity index 65% rename from playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.dart rename to playground/frontend/playground_components/test/src/repositories/code_repository_test.dart index 344840d29d80..757e4f57ba3f 100644 --- a/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.dart +++ b/playground/frontend/playground_components/test/src/repositories/code_repository_test.dart @@ -19,20 +19,20 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_client/run_code_response.dart'; -import 'package:playground/modules/editor/repository/code_repository/code_repository.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'; -import 'package:playground/modules/editor/repository/code_repository/run_code_result.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/src/models/sdk.dart'; +import 'package:playground_components/src/repositories/code_client/code_client.dart'; +import 'package:playground_components/src/repositories/code_repository.dart'; +import 'package:playground_components/src/repositories/models/check_status_response.dart'; +import 'package:playground_components/src/repositories/models/output_response.dart'; +import 'package:playground_components/src/repositories/models/run_code_request.dart'; +import 'package:playground_components/src/repositories/models/run_code_response.dart'; +import 'package:playground_components/src/repositories/models/run_code_result.dart'; import 'code_repository_test.mocks.dart'; -final kRequestMock = RunCodeRequestWrapper( +const kRequestMock = RunCodeRequest( code: 'code', - sdk: SDK.java, + sdk: Sdk.java, pipelineOptions: {}, ); @@ -45,25 +45,25 @@ const kRunErrorOutput = 'RunErrorOutput'; const kPreparationErrorOutput = 'PreparationErrorOutput'; const kValidationErrorOutput = 'ValidationErrorOutput'; -final kRunCodeResponse = RunCodeResponse(kPipelineUuid); -final kFinishedStatusResponse = CheckStatusResponse(RunCodeStatus.finished); -final kErrorStatusResponse = CheckStatusResponse(RunCodeStatus.unknownError); -final kRunErrorStatusResponse = CheckStatusResponse(RunCodeStatus.runError); -final kExecutingStatusResponse = CheckStatusResponse(RunCodeStatus.executing); -final kCompileErrorStatusResponse = - CheckStatusResponse(RunCodeStatus.compileError); -final kValidationErrorStatusResponse = - CheckStatusResponse(RunCodeStatus.validationError); -final kPreparationErrorStatusResponse = - CheckStatusResponse(RunCodeStatus.preparationError); +const kRunCodeResponse = RunCodeResponse(pipelineUuid: kPipelineUuid); +const kFinishedStatusResponse = CheckStatusResponse(status: RunCodeStatus.finished,); +const kErrorStatusResponse = CheckStatusResponse(status: RunCodeStatus.unknownError,); +const kRunErrorStatusResponse = CheckStatusResponse(status: RunCodeStatus.runError,); +const kExecutingStatusResponse = CheckStatusResponse(status: RunCodeStatus.executing,); +const kCompileErrorStatusResponse = + CheckStatusResponse(status: RunCodeStatus.compileError,); +const kValidationErrorStatusResponse = + CheckStatusResponse(status: RunCodeStatus.validationError,); +const kPreparationErrorStatusResponse = + CheckStatusResponse(status: RunCodeStatus.preparationError,); -final kRunOutputResponse = OutputResponse(kRunOutput); -final kLogOutputResponse = OutputResponse(kLogOutput); -final kCompileOutputResponse = OutputResponse(kCompileOutput); -final kRunErrorOutputResponse = OutputResponse(kRunErrorOutput); -final kGraphResponse = OutputResponse(kGraphOutput); -final kValidationErrorOutputResponse = OutputResponse(kValidationErrorOutput); -final kPreparationErrorOutputResponse = OutputResponse(kPreparationErrorOutput); +const kRunOutputResponse = OutputResponse(output: kRunOutput); +const kLogOutputResponse = OutputResponse(output: kLogOutput); +const kCompileOutputResponse = OutputResponse(output: kCompileOutput); +const kRunErrorOutputResponse = OutputResponse(output: kRunErrorOutput); +const kGraphResponse = OutputResponse(output: kGraphOutput); +const kValidationErrorOutputResponse = OutputResponse(output: kValidationErrorOutput); +const kPreparationErrorOutputResponse = OutputResponse(output: kPreparationErrorOutput); @GenerateMocks([CodeClient]) void main() { @@ -74,27 +74,27 @@ void main() { when(client.runCode(kRequestMock)).thenAnswer( (_) async => kRunCodeResponse, ); - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => kFinishedStatusResponse, ); - when(client.getRunOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunOutput(kPipelineUuid)).thenAnswer( (_) async => kRunOutputResponse, ); - when(client.getCompileOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getCompileOutput(kPipelineUuid)).thenAnswer( (_) async => kCompileOutputResponse, ); - when(client.getRunErrorOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunErrorOutput(kPipelineUuid)).thenAnswer( (_) async => kRunErrorOutputResponse, ); - when(client.getLogOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getLogOutput(kPipelineUuid)).thenAnswer( (_) async => kLogOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion @@ -115,7 +115,7 @@ void main() { ]), ); // compile output should not be called - verifyNever(client.getCompileOutput(kPipelineUuid, kRequestMock)); + verifyNever(client.getCompileOutput(kPipelineUuid)); }); test('should return output from compilation if failed', () async { @@ -124,24 +124,24 @@ void main() { when(client.runCode(kRequestMock)).thenAnswer( (_) async => kRunCodeResponse, ); - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => kCompileErrorStatusResponse, ); - when(client.getCompileOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getCompileOutput(kPipelineUuid)).thenAnswer( (_) async => kCompileOutputResponse, ); - when(client.getRunOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunOutput(kPipelineUuid)).thenAnswer( (_) async => kRunOutputResponse, ); - when(client.getLogOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getLogOutput(kPipelineUuid)).thenAnswer( (_) async => kLogOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion @@ -170,19 +170,19 @@ void main() { when(client.runCode(kRequestMock)).thenAnswer( (_) async => kRunCodeResponse, ); - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => kValidationErrorStatusResponse, ); - when(client.getValidationErrorOutput(kPipelineUuid, kRequestMock)) + when(client.getValidationErrorOutput(kPipelineUuid)) .thenAnswer( (_) async => kValidationErrorOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion @@ -210,19 +210,19 @@ void main() { when(client.runCode(kRequestMock)).thenAnswer( (_) async => kRunCodeResponse, ); - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => kPreparationErrorStatusResponse, ); - when(client.getPreparationErrorOutput(kPipelineUuid, kRequestMock)) + when(client.getPreparationErrorOutput(kPipelineUuid)) .thenAnswer( (_) async => kPreparationErrorOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion @@ -250,27 +250,27 @@ void main() { when(client.runCode(kRequestMock)).thenAnswer( (_) async => kRunCodeResponse, ); - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => kRunErrorStatusResponse, ); - when(client.getCompileOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getCompileOutput(kPipelineUuid)).thenAnswer( (_) async => kCompileOutputResponse, ); - when(client.getRunOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunOutput(kPipelineUuid)).thenAnswer( (_) async => kRunOutputResponse, ); - when(client.getRunErrorOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunErrorOutput(kPipelineUuid)).thenAnswer( (_) async => kRunErrorOutputResponse, ); - when(client.getLogOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getLogOutput(kPipelineUuid)).thenAnswer( (_) async => kLogOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion @@ -305,24 +305,24 @@ void main() { kExecutingStatusResponse, kFinishedStatusResponse ]; - when(client.checkStatus(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.checkStatus(kPipelineUuid)).thenAnswer( (_) async => answers.removeAt(0), ); - when(client.getRunOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunOutput(kPipelineUuid)).thenAnswer( (_) async => kRunOutputResponse, ); - when(client.getRunErrorOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getRunErrorOutput(kPipelineUuid)).thenAnswer( (_) async => kRunErrorOutputResponse, ); - when(client.getLogOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getLogOutput(kPipelineUuid)).thenAnswer( (_) async => kLogOutputResponse, ); - when(client.getGraphOutput(kPipelineUuid, kRequestMock)).thenAnswer( + when(client.getGraphOutput(kPipelineUuid)).thenAnswer( (_) async => kGraphResponse, ); // test variables - final repository = CodeRepository(client); + final repository = CodeRepository(client: client); final stream = repository.runCode(kRequestMock); // test assertion diff --git a/playground/frontend/playground_components/test/src/repositories/example_repository_test.dart b/playground/frontend/playground_components/test/src/repositories/example_repository_test.dart new file mode 100644 index 000000000000..7046f1e553c6 --- /dev/null +++ b/playground/frontend/playground_components/test/src/repositories/example_repository_test.dart @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:playground_components/src/repositories/example_client/example_client.dart'; +import 'package:playground_components/src/repositories/example_repository.dart'; + +import '../common/requests.dart'; +import 'example_repository_test.mocks.dart'; + +@GenerateMocks([ExampleClient]) +void main() { + late ExampleRepository repo; + late ExampleClient client; + + setUp( + () { + client = MockExampleClient(); + repo = ExampleRepository(client: client); + }, + ); + + test( + 'Example repository getListOfExamples should return response with categories', + () async { + when(client.getPrecompiledObjects(kGetPrecompiledObjectsRequest)) + .thenAnswer((_) async => kGetPrecompiledObjectsResponse); + expect( + await repo.getListOfExamples(kGetPrecompiledObjectsRequest), + kGetPrecompiledObjectsResponse.categories, + ); + verify(client.getPrecompiledObjects(kGetPrecompiledObjectsRequest)).called(1); + }, + ); + + test( + 'Example repository getDefaultExample should return defaultExample for chosen Sdk', + () async { + when(client.getDefaultPrecompiledObject(kGetDefaultPrecompiledObjectRequest)) + .thenAnswer((_) async => kGetDefaultPrecompiledObjectResponse); + expect( + await repo.getDefaultExample(kGetDefaultPrecompiledObjectRequest), + kGetDefaultPrecompiledObjectResponse.example, + ); + verify(client.getDefaultPrecompiledObject(kGetDefaultPrecompiledObjectRequest)).called(1); + }, + ); + + test( + 'Example repository getExampleSource should return source code for example', + () async { + when(client.getPrecompiledObjectCode(kRequestForExampleInfo)) + .thenAnswer((_) async => kGetPrecompiledObjectCodeResponse); + expect( + await repo.getExampleSource(kRequestForExampleInfo), + kGetPrecompiledObjectCodeResponse.code, + ); + verify(client.getPrecompiledObjectCode(kRequestForExampleInfo)).called(1); + }, + ); + + test( + 'Example repository getExampleOutput should return output for example', + () async { + when(client.getPrecompiledObjectOutput(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse); + expect( + await repo.getExampleOutput(kRequestForExampleInfo), + kOutputResponse.output, + ); + verify(client.getPrecompiledObjectOutput(kRequestForExampleInfo)).called(1); + }, + ); + + test( + 'Example repository getExampleLogs should return logs for example', + () async { + when(client.getPrecompiledObjectLogs(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse); + expect( + await repo.getExampleLogs(kRequestForExampleInfo), + kOutputResponse.output, + ); + verify(client.getPrecompiledObjectLogs(kRequestForExampleInfo)).called(1); + }, + ); + + test( + 'Example repository getExampleGraph should return logs for example', + () async { + when(client.getPrecompiledObjectGraph(kRequestForExampleInfo)) + .thenAnswer((_) async => kOutputResponse); + expect( + await repo.getExampleGraph(kRequestForExampleInfo), + kOutputResponse.output, + ); + verify(client.getPrecompiledObjectGraph(kRequestForExampleInfo)).called(1); + }, + ); + + test( + 'Example repository getExample should return ExampleModel', + () async { + when(client.getPrecompiledObject(kRequestForExampleInfo)) + .thenAnswer((_) async => kGetDefaultPrecompiledObjectResponse); + expect( + await repo.getExample(kRequestForExampleInfo), + kGetDefaultPrecompiledObjectResponse.example, + ); + verify(client.getPrecompiledObject(kRequestForExampleInfo)).called(1); + }, + ); +} diff --git a/playground/frontend/test/modules/editor/parsers/run_options_parser_test.dart b/playground/frontend/playground_components/test/src/util/pipeline_options_test.dart similarity index 96% rename from playground/frontend/test/modules/editor/parsers/run_options_parser_test.dart rename to playground/frontend/playground_components/test/src/util/pipeline_options_test.dart index 26657af6cc6c..cb15e5b046e6 100644 --- a/playground/frontend/test/modules/editor/parsers/run_options_parser_test.dart +++ b/playground/frontend/playground_components/test/src/util/pipeline_options_test.dart @@ -17,7 +17,7 @@ */ import 'package:flutter_test/flutter_test.dart'; -import 'package:playground/modules/editor/parsers/run_options_parser.dart'; +import 'package:playground_components/src/util/pipeline_options.dart'; void main() { group('PipelineOptions parser', () { diff --git a/playground/frontend/test/utils/run_with_retry_test.dart b/playground/frontend/playground_components/test/src/util/run_with_retry_test.dart similarity index 97% rename from playground/frontend/test/utils/run_with_retry_test.dart rename to playground/frontend/playground_components/test/src/util/run_with_retry_test.dart index 39d7a9ddb6b6..5c655a01f3c4 100644 --- a/playground/frontend/test/utils/run_with_retry_test.dart +++ b/playground/frontend/playground_components/test/src/util/run_with_retry_test.dart @@ -17,7 +17,7 @@ */ import 'package:flutter_test/flutter_test.dart'; -import 'package:playground/utils/run_with_retry.dart'; +import 'package:playground_components/src/util/run_with_retry.dart'; class ExecutionTime { final int time; diff --git a/playground/frontend/pubspec.lock b/playground/frontend/pubspec.lock index eb1df6618a43..757308ffe2c9 100644 --- a/playground/frontend/pubspec.lock +++ b/playground/frontend/pubspec.lock @@ -35,7 +35,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "3.3.1" args: dependency: transitive description: @@ -49,7 +49,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -119,7 +119,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" charcode: dependency: transitive description: @@ -140,7 +140,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: @@ -149,7 +149,7 @@ packages: source: hosted version: "4.1.0" code_text_field: - dependency: "direct main" + dependency: transitive description: path: "." ref: "9e2c9fe52a69481f038f4b6609e8a0a776429437" @@ -185,6 +185,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.17.2" + csv: + dependency: transitive + description: + name: csv + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1" dart_style: dependency: transitive description: @@ -192,6 +199,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + easy_localization_ext: + dependency: "direct main" + description: + name: easy_localization_ext + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + easy_localization_loader: + dependency: "direct main" + description: + name: easy_localization_loader + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + easy_logger: + dependency: transitive + description: + name: easy_logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" expansion_widget: dependency: "direct main" description: @@ -205,7 +247,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" ffi: dependency: transitive description: @@ -257,7 +299,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.22.0" + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -283,12 +325,12 @@ packages: source: hosted version: "2.0.2" google_fonts: - dependency: "direct main" + dependency: transitive description: name: google_fonts url: "https://pub.dartlang.org" source: hosted - version: "2.3.3" + version: "3.0.1" googleapis_auth: dependency: transitive description: @@ -304,14 +346,14 @@ packages: source: hosted version: "2.1.0" grpc: - dependency: "direct main" + dependency: transitive description: name: grpc url: "https://pub.dartlang.org" source: hosted version: "3.0.2" highlight: - dependency: "direct main" + dependency: transitive description: name: highlight url: "https://pub.dartlang.org" @@ -407,21 +449,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" mime: dependency: transitive description: @@ -463,21 +505,21 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" path_drawing: dependency: transitive description: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.5.1+1" + version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "0.2.1" + version: "1.0.1" path_provider: dependency: transitive description: @@ -541,6 +583,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0" + playground_components: + dependency: "direct main" + description: + path: playground_components + relative: true + source: path + version: "0.0.1" plugin_platform_interface: dependency: transitive description: @@ -568,7 +617,7 @@ packages: name: protobuf url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.1.0" provider: dependency: "direct main" description: @@ -678,7 +727,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -706,21 +755,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" timing: dependency: transitive description: @@ -805,6 +854,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + url_strategy: + dependency: "direct main" + description: + name: url_strategy + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" usage: dependency: "direct main" description: @@ -869,5 +925,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.17.0 <3.0.0" - flutter: ">=3.0.0" + dart: ">=2.17.6 <3.0.0" + flutter: ">=3.3.1" diff --git a/playground/frontend/pubspec.yaml b/playground/frontend/pubspec.yaml index 91027ecd99fc..b989f7fe4443 100644 --- a/playground/frontend/pubspec.yaml +++ b/playground/frontend/pubspec.yaml @@ -22,28 +22,28 @@ version: 1.0.0+1 environment: sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.3.1" dependencies: akvelon_flutter_issue_106664_workaround: ^0.1.2 aligned_dialog: ^0.0.6 - code_text_field: - git: - url: https://github.com/BertrandBev/code_field.git - ref: 9e2c9fe52a69481f038f4b6609e8a0a776429437 collection: ^1.15.0 + easy_localization: ^3.0.1 + easy_localization_ext: ^0.1.1 + easy_localization_loader: ^1.0.0 expansion_widget: ^0.0.2 flutter: { sdk: flutter } flutter_localizations: { sdk: flutter } - flutter_svg: ^0.22.0 + flutter_svg: ^1.0.3 flutter_web_plugins: { sdk: flutter } - google_fonts: ^2.3.1 - grpc: ^3.0.0 - highlight: ^0.7.0 + google_fonts: ^3.0.1 intl: ^0.17.0 onmessage: ^0.2.0 + playground_components: { path: playground_components } provider: ^6.0.0 shared_preferences: ^2.0.12 url_launcher: ^6.0.12 + url_strategy: ^0.2.0 usage: ^4.0.2 dev_dependencies: @@ -56,6 +56,7 @@ dev_dependencies: flutter: assets: - assets/ + - assets/translations/en.yaml generate: true uses-material-design: true diff --git a/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart b/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart deleted file mode 100644 index b2429f18816c..000000000000 --- a/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// Mocks generated by Mockito 5.0.16 from annotations -// in playground/test/modules/editor/repository/code_repository/code_repository_test.dart. -// Do not manually edit this file. - -import 'dart:async' as _i6; - -import 'package:mockito/mockito.dart' as _i1; -import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart' - as _i3; -import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart' - as _i5; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart' - as _i4; -import 'package:playground/modules/editor/repository/code_repository/code_client/run_code_response.dart' - as _i2; -import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart' - as _i7; - -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types - -class _FakeRunCodeResponse_0 extends _i1.Fake implements _i2.RunCodeResponse {} - -class _FakeCheckStatusResponse_1 extends _i1.Fake - implements _i3.CheckStatusResponse {} - -class _FakeOutputResponse_2 extends _i1.Fake implements _i4.OutputResponse {} - -/// A class which mocks [CodeClient]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockCodeClient extends _i1.Mock implements _i5.CodeClient { - MockCodeClient() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i2.RunCodeResponse> runCode(_i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#runCode, [request]), - returnValue: - Future<_i2.RunCodeResponse>.value(_FakeRunCodeResponse_0())) - as _i6.Future<_i2.RunCodeResponse>); - @override - _i6.Future cancelExecution(String? pipelineUuid) => - (super.noSuchMethod(Invocation.method(#cancelExecution, [pipelineUuid]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future<_i3.CheckStatusResponse> checkStatus( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#checkStatus, [pipelineUuid, request]), - returnValue: Future<_i3.CheckStatusResponse>.value( - _FakeCheckStatusResponse_1())) - as _i6.Future<_i3.CheckStatusResponse>); - @override - _i6.Future<_i4.OutputResponse> getCompileOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getCompileOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getRunOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getRunOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getLogOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getLogOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getRunErrorOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getRunErrorOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getValidationErrorOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getValidationErrorOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) as _i6 - .Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getPreparationErrorOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method( - #getPreparationErrorOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - _i6.Future<_i4.OutputResponse> getGraphOutput( - String? pipelineUuid, _i7.RunCodeRequestWrapper? request) => - (super.noSuchMethod( - Invocation.method(#getGraphOutput, [pipelineUuid, request]), - returnValue: - Future<_i4.OutputResponse>.value(_FakeOutputResponse_2())) - as _i6.Future<_i4.OutputResponse>); - @override - String toString() => super.toString(); -} diff --git a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.dart b/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.dart deleted file mode 100644 index 5e08b3773847..000000000000 --- a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.dart +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:playground/modules/examples/repositories/example_client/example_client.dart'; -import 'package:playground/modules/examples/repositories/example_repository.dart'; - -import '../../../../pages/playground/states/mocks/request_mock.dart'; -import 'example_repository_test.mocks.dart'; - -@GenerateMocks([ExampleClient]) -void main() { - late ExampleRepository repo; - late ExampleClient client; - - setUp( - () { - client = MockExampleClient(); - repo = ExampleRepository(client); - }, - ); - - test( - 'Example repository getListOfExamples should return response with categories', - () async { - when(client.getListOfExamples(kGetListOfExamplesRequestMock)) - .thenAnswer((_) async => kGetListOfExamplesResponseMock); - expect( - await repo.getListOfExamples(kGetListOfExamplesRequestMock), - kGetListOfExamplesResponseMock.categories, - ); - verify(client.getListOfExamples(kGetListOfExamplesRequestMock)).called(1); - }, - ); - - test( - 'Example repository getDefaultExample should return defaultExample for chosen Sdk', - () async { - when(client.getDefaultExample(kGetExampleRequestMock)) - .thenAnswer((_) async => kGetExampleResponseMock); - expect( - await repo.getDefaultExample(kGetExampleRequestMock), - kGetExampleResponseMock.example, - ); - verify(client.getDefaultExample(kGetExampleRequestMock)).called(1); - }, - ); - - test( - 'Example repository getExampleSource should return source code for example', - () async { - when(client.getExampleSource(kGetExampleRequestMock)) - .thenAnswer((_) async => kGetExampleCodeResponseMock); - expect( - await repo.getExampleSource(kGetExampleRequestMock), - kGetExampleCodeResponseMock.code, - ); - verify(client.getExampleSource(kGetExampleRequestMock)).called(1); - }, - ); - - test( - 'Example repository getExampleOutput should return output for example', - () async { - when(client.getExampleOutput(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock); - expect( - await repo.getExampleOutput(kGetExampleRequestMock), - kOutputResponseMock.output, - ); - verify(client.getExampleOutput(kGetExampleRequestMock)).called(1); - }, - ); - - test( - 'Example repository getExampleLogs should return logs for example', - () async { - when(client.getExampleLogs(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock); - expect( - await repo.getExampleLogs(kGetExampleRequestMock), - kOutputResponseMock.output, - ); - verify(client.getExampleLogs(kGetExampleRequestMock)).called(1); - }, - ); - - test( - 'Example repository getExampleLogs should return logs for example', - () async { - when(client.getExampleGraph(kGetExampleRequestMock)) - .thenAnswer((_) async => kOutputResponseMock); - expect( - await repo.getExampleGraph(kGetExampleRequestMock), - kOutputResponseMock.output, - ); - verify(client.getExampleGraph(kGetExampleRequestMock)).called(1); - }, - ); - - test( - 'Example repository getExample should return ExampleModel', - () async { - when(client.getExample(kGetExampleRequestMock)) - .thenAnswer((_) async => kGetExampleResponseMock); - expect( - await repo.getExample(kGetExampleRequestMock), - kGetExampleResponseMock.example, - ); - verify(client.getExample(kGetExampleRequestMock)).called(1); - }, - ); -} diff --git a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart b/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart deleted file mode 100644 index 92bcb2546a5a..000000000000 --- a/playground/frontend/test/modules/editor/repository/example_repository/example_repository_test.mocks.dart +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Mocks generated by Mockito 5.1.0 from annotations -// in playground/test/modules/editor/repository/example_repository/example_repository_test.dart. -// Do not manually edit this file. - -import 'dart:async' as _i9; - -import 'package:mockito/mockito.dart' as _i1; -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart' - as _i5; -import 'package:playground/modules/examples/repositories/example_client/example_client.dart' - as _i8; -import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart' - as _i3; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart' - as _i11; -import 'package:playground/modules/examples/repositories/models/get_example_response.dart' - as _i4; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart' - as _i10; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart' - as _i2; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart' - as _i12; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart' - as _i6; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart' - as _i13; -import 'package:playground/modules/examples/repositories/models/save_snippet_response.dart' - as _i7; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types - -class _FakeGetListOfExampleResponse_0 extends _i1.Fake - implements _i2.GetListOfExampleResponse {} - -class _FakeGetExampleCodeResponse_1 extends _i1.Fake - implements _i3.GetExampleCodeResponse {} - -class _FakeGetExampleResponse_2 extends _i1.Fake - implements _i4.GetExampleResponse {} - -class _FakeOutputResponse_3 extends _i1.Fake implements _i5.OutputResponse {} - -class _FakeGetSnippetResponse_4 extends _i1.Fake - implements _i6.GetSnippetResponse {} - -class _FakeSaveSnippetResponse_5 extends _i1.Fake - implements _i7.SaveSnippetResponse {} - -/// A class which mocks [ExampleClient]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockExampleClient extends _i1.Mock implements _i8.ExampleClient { - MockExampleClient() { - _i1.throwOnMissingStub(this); - } - - @override - _i9.Future<_i2.GetListOfExampleResponse> getListOfExamples( - _i10.GetListOfExamplesRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getListOfExamples, [request]), - returnValue: Future<_i2.GetListOfExampleResponse>.value( - _FakeGetListOfExampleResponse_0())) - as _i9.Future<_i2.GetListOfExampleResponse>); - @override - _i9.Future<_i3.GetExampleCodeResponse> getExampleSource( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleSource, [request]), - returnValue: Future<_i3.GetExampleCodeResponse>.value( - _FakeGetExampleCodeResponse_1())) - as _i9.Future<_i3.GetExampleCodeResponse>); - @override - _i9.Future<_i4.GetExampleResponse> getDefaultExample( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getDefaultExample, [request]), - returnValue: Future<_i4.GetExampleResponse>.value( - _FakeGetExampleResponse_2())) - as _i9.Future<_i4.GetExampleResponse>); - @override - _i9.Future<_i4.GetExampleResponse> getExample( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExample, [request]), - returnValue: Future<_i4.GetExampleResponse>.value( - _FakeGetExampleResponse_2())) - as _i9.Future<_i4.GetExampleResponse>); - @override - _i9.Future<_i5.OutputResponse> getExampleOutput( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleOutput, [request]), - returnValue: - Future<_i5.OutputResponse>.value(_FakeOutputResponse_3())) - as _i9.Future<_i5.OutputResponse>); - @override - _i9.Future<_i5.OutputResponse> getExampleLogs( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleLogs, [request]), - returnValue: - Future<_i5.OutputResponse>.value(_FakeOutputResponse_3())) - as _i9.Future<_i5.OutputResponse>); - @override - _i9.Future<_i5.OutputResponse> getExampleGraph( - _i11.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleGraph, [request]), - returnValue: - Future<_i5.OutputResponse>.value(_FakeOutputResponse_3())) - as _i9.Future<_i5.OutputResponse>); - @override - _i9.Future<_i6.GetSnippetResponse> getSnippet( - _i12.GetSnippetRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getSnippet, [request]), - returnValue: Future<_i6.GetSnippetResponse>.value( - _FakeGetSnippetResponse_4())) - as _i9.Future<_i6.GetSnippetResponse>); - @override - _i9.Future<_i7.SaveSnippetResponse> saveSnippet( - _i13.SaveSnippetRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#saveSnippet, [request]), - returnValue: Future<_i7.SaveSnippetResponse>.value( - _FakeSaveSnippetResponse_5())) - as _i9.Future<_i7.SaveSnippetResponse>); -} diff --git a/playground/frontend/test/modules/messages/handlers/messages_debouncer_test.dart b/playground/frontend/test/modules/messages/handlers/messages_debouncer_test.dart index df5266c23dd5..7999493bea33 100644 --- a/playground/frontend/test/modules/messages/handlers/messages_debouncer_test.dart +++ b/playground/frontend/test/modules/messages/handlers/messages_debouncer_test.dart @@ -24,7 +24,7 @@ import 'package:playground/modules/messages/handlers/abstract_message_handler.da import 'package:playground/modules/messages/handlers/messages_debouncer.dart'; import 'package:playground/modules/messages/models/abstract_message.dart'; import 'package:playground/modules/messages/models/set_sdk_message.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; void main() { group('MessagesDebouncer', () { @@ -38,13 +38,13 @@ void main() { test('drops sequential calls, no time limit', () { fakeAsync((async) { - debouncer.handle(SetSdkMessage(sdk: SDK.java)); - debouncer.handle(SetSdkMessage(sdk: SDK.java)); + debouncer.handle(SetSdkMessage(sdk: Sdk.java)); + debouncer.handle(SetSdkMessage(sdk: Sdk.java)); async.elapse(const Duration(days: 36500)); - debouncer.handle(SetSdkMessage(sdk: SDK.java)); + debouncer.handle(SetSdkMessage(sdk: Sdk.java)); }); - expect(recorder.messages, [SetSdkMessage(sdk: SDK.java)]); + expect(recorder.messages, [SetSdkMessage(sdk: Sdk.java)]); }); test('returns the last result on debouncing', () { diff --git a/playground/frontend/test/modules/messages/models/set_content_message_test.dart b/playground/frontend/test/modules/messages/models/set_content_message_test.dart index c472a8269e44..a552501b84a9 100644 --- a/playground/frontend/test/modules/messages/models/set_content_message_test.dart +++ b/playground/frontend/test/modules/messages/models/set_content_message_test.dart @@ -17,14 +17,11 @@ */ import 'package:flutter_test/flutter_test.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/content_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; import 'package:playground/modules/messages/models/set_content_message.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; const _content = 'my_code'; -const _sdk = SDK.python; +const _sdk = Sdk.python; void main() { group('SetContentMessage.tryParse', () { @@ -50,7 +47,7 @@ void main() { parsed, const SetContentMessage( descriptor: ExamplesLoadingDescriptor( - descriptors: [EmptyExampleLoadingDescriptor(sdk: SDK.java)], + descriptors: [EmptyExampleLoadingDescriptor(sdk: Sdk.java)], ), ), ); @@ -71,7 +68,7 @@ void main() { parsed, const SetContentMessage( descriptor: ExamplesLoadingDescriptor( - descriptors: [EmptyExampleLoadingDescriptor(sdk: SDK.java)], + descriptors: [EmptyExampleLoadingDescriptor(sdk: Sdk.java)], ), ), ); @@ -94,16 +91,16 @@ void main() { { 'content': _content, 'name': 'name', - 'sdk': _sdk.name, + 'sdk': _sdk.id, }, { 'content': _content, 'name': null, - 'sdk': _sdk.name, + 'sdk': _sdk.id, }, { 'content': _content, - 'sdk': _sdk.name, + 'sdk': _sdk.id, }, ], }, diff --git a/playground/frontend/test/modules/messages/models/set_sdk_message_test.dart b/playground/frontend/test/modules/messages/models/set_sdk_message_test.dart index e608a5b5c024..c9223849bbfe 100644 --- a/playground/frontend/test/modules/messages/models/set_sdk_message_test.dart +++ b/playground/frontend/test/modules/messages/models/set_sdk_message_test.dart @@ -18,9 +18,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:playground/modules/messages/models/set_sdk_message.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; -const _sdk = SDK.python; +const _sdk = Sdk.python; void main() { group('SetSdkMessage.tryParse', () { @@ -49,7 +49,7 @@ void main() { test( 'parses an SDK', () { - final map = {'type': SetSdkMessage.type, 'sdk': _sdk.name}; + final map = {'type': SetSdkMessage.type, 'sdk': _sdk.id}; final parsed = SetSdkMessage.tryParse(map); diff --git a/playground/frontend/test/modules/messages/parsers/message_parser_test.dart b/playground/frontend/test/modules/messages/parsers/message_parser_test.dart index 24173cece319..00b580143457 100644 --- a/playground/frontend/test/modules/messages/parsers/message_parser_test.dart +++ b/playground/frontend/test/modules/messages/parsers/message_parser_test.dart @@ -17,14 +17,12 @@ */ import 'package:flutter_test/flutter_test.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/empty_example_loading_descriptor.dart'; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart'; import 'package:playground/modules/messages/models/set_content_message.dart'; import 'package:playground/modules/messages/models/set_sdk_message.dart'; import 'package:playground/modules/messages/parsers/messages_parser.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; +import 'package:playground_components/playground_components.dart'; -const _sdk = SDK.python; +const _sdk = Sdk.python; void main() { group('MessageParser.parse returns null for invalid inputs', () { @@ -96,7 +94,7 @@ void main() { parsed, const SetContentMessage( descriptor: ExamplesLoadingDescriptor( - descriptors: [EmptyExampleLoadingDescriptor(sdk: SDK.java)], + descriptors: [EmptyExampleLoadingDescriptor(sdk: Sdk.java)], ), ), ); @@ -106,7 +104,7 @@ void main() { test( 'MessageParser.parse parses SetSdkMessage', () { - final value = {'type': SetSdkMessage.type, 'sdk': _sdk.name}; + final value = {'type': SetSdkMessage.type, 'sdk': _sdk.id}; final parsed = MessagesParser().tryParse(value); diff --git a/playground/frontend/test/pages/playground/states/example_selector_state_test.dart b/playground/frontend/test/pages/playground/states/example_selector_state_test.dart index 99d847f38e07..1d1c37d4d957 100644 --- a/playground/frontend/test/pages/playground/states/example_selector_state_test.dart +++ b/playground/frontend/test/pages/playground/states/example_selector_state_test.dart @@ -18,30 +18,34 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart'; +import 'package:playground_components/src/models/example_base.dart'; +import 'package:playground_components/src/controllers/example_loaders/examples_loader.dart'; import 'package:playground/pages/playground/states/example_selector_state.dart'; -import 'package:playground/pages/playground/states/examples_state.dart'; -import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:playground_components/src/cache/example_cache.dart'; +import 'package:playground_components/src/controllers/playground_controller.dart'; import 'example_selector_state_test.mocks.dart'; -import 'mocks/categories_mock.dart'; -import 'mocks/example_repository_mock.dart'; +import '../../../../playground_components/test/src/common/categories.dart'; +import '../../../../playground_components/test/src/common/example_repository_mock.dart'; @GenerateMocks([ExamplesLoader]) void main() { - late PlaygroundState playgroundState; - late ExampleState exampleState; + late PlaygroundController playgroundController; + late ExampleCache exampleCache; late ExampleSelectorState state; final mockExampleRepository = getMockExampleRepository(); setUp(() { - exampleState = ExampleState(mockExampleRepository); - playgroundState = PlaygroundState( + exampleCache = ExampleCache( + exampleRepository: mockExampleRepository, + hasCatalog: true, + ); + + playgroundController = PlaygroundController( examplesLoader: MockExamplesLoader(), - exampleState: exampleState, + exampleCache: exampleCache, ); - state = ExampleSelectorState(playgroundState, []); + state = ExampleSelectorState(playgroundController, []); }); test( @@ -95,7 +99,7 @@ void main() { '- affect Example state categories', () { state.addListener(() { expect(state.categories, []); - expect(exampleState.sdkCategories, exampleState.sdkCategories); + expect(exampleCache.categoryListsBySdk, exampleCache.categoryListsBySdk); }); state.sortCategories(); }); @@ -107,12 +111,12 @@ void main() { 'but should NOT:' '- affect Example state categories', () { final state = ExampleSelectorState( - playgroundState, + playgroundController, categoriesMock, ); state.addListener(() { expect(state.categories, examplesSortedByTypeMock); - expect(exampleState.sdkCategories, exampleState.sdkCategories); + expect(exampleCache.categoryListsBySdk, exampleCache.categoryListsBySdk); }); state.sortExamplesByType(unsortedExamples, ExampleType.kata); }); @@ -126,12 +130,12 @@ void main() { '- be sensitive for register,' '- affect Example state categories', () { final state = ExampleSelectorState( - playgroundState, + playgroundController, categoriesMock, ); state.addListener(() { expect(state.categories, examplesSortedByNameMock); - expect(exampleState.sdkCategories, exampleState.sdkCategories); + expect(exampleCache.categoryListsBySdk, exampleCache.categoryListsBySdk); }); state.sortExamplesByName(unsortedExamples, 'X1'); }); diff --git a/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart b/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart index 253c7b5d4aac..d6ceb58247ea 100644 --- a/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart +++ b/playground/frontend/test/pages/playground/states/example_selector_state_test.mocks.dart @@ -19,17 +19,20 @@ // in playground/test/pages/playground/states/example_selector_state_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; +import 'dart:async' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart' - as _i6; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart' - as _i5; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart' +import 'package:playground_components/src/controllers/example_loaders/example_loader_factory.dart' as _i2; -import 'package:playground/pages/playground/states/playground_state.dart' +import 'package:playground_components/src/controllers/example_loaders/examples_loader.dart' as _i3; +import 'package:playground_components/src/controllers/playground_controller.dart' + as _i4; +import 'package:playground_components/src/models/example_loading_descriptors/example_loading_descriptor.dart' + as _i8; +import 'package:playground_components/src/models/example_loading_descriptors/examples_loading_descriptor.dart' + as _i6; +import 'package:playground_components/src/models/sdk.dart' as _i7; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -41,29 +44,41 @@ import 'package:playground/pages/playground/states/playground_state.dart' // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +class _FakeExampleLoaderFactory_0 extends _i1.Fake + implements _i2.ExampleLoaderFactory {} + /// A class which mocks [ExamplesLoader]. /// /// See the documentation for Mockito's code generation for more information. -class MockExamplesLoader extends _i1.Mock implements _i2.ExamplesLoader { +class MockExamplesLoader extends _i1.Mock implements _i3.ExamplesLoader { MockExamplesLoader() { _i1.throwOnMissingStub(this); } @override - void setPlaygroundState(_i3.PlaygroundState? value) => - super.noSuchMethod(Invocation.method(#setPlaygroundState, [value]), + _i2.ExampleLoaderFactory get defaultFactory => (super.noSuchMethod( + Invocation.getter(#defaultFactory), + returnValue: _FakeExampleLoaderFactory_0()) as _i2.ExampleLoaderFactory); + @override + void setPlaygroundController(_i4.PlaygroundController? value) => + super.noSuchMethod(Invocation.method(#setPlaygroundController, [value]), returnValueForMissingStub: null); @override - _i4.Future load(_i5.ExamplesLoadingDescriptor? descriptor) => + _i5.Future load(_i6.ExamplesLoadingDescriptor? descriptor) => (super.noSuchMethod(Invocation.method(#load, [descriptor]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadDefaultIfAny(_i7.Sdk? sdk) => + (super.noSuchMethod(Invocation.method(#loadDefaultIfAny, [sdk]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadOne( - {_i5.ExamplesLoadingDescriptor? group, - _i6.ExampleLoadingDescriptor? one}) => + _i5.Future loadOne( + {_i6.ExamplesLoadingDescriptor? group, + _i8.ExampleLoadingDescriptor? one}) => (super.noSuchMethod( Invocation.method(#loadOne, [], {#group: group, #one: one}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } diff --git a/playground/frontend/test/pages/playground/states/mocks/categories_mock.dart b/playground/frontend/test/pages/playground/states/mocks/categories_mock.dart deleted file mode 100644 index 0079697013c0..000000000000 --- a/playground/frontend/test/pages/playground/states/mocks/categories_mock.dart +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/examples/models/category_model.dart'; -import 'package:playground/modules/examples/models/example_model.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -import 'example_mock.dart'; - -final categoriesMock = [ - CategoryModel(name: 'Sorted', examples: [exampleMock1]), - CategoryModel(name: 'Unsorted', examples: [exampleMock2]), -]; - -final List sortedCategories = [ - CategoryModel(name: 'Sorted', examples: [exampleMock1]), -]; - -final List unsortedExamples = [exampleMock1, exampleMock2]; - -final List examplesSortedByTypeMock = [exampleMock2]; - -final List examplesSortedByNameMock = [exampleMock1]; - -final sdkCategoriesFromServerMock = { - SDK.java: categoriesMock, - SDK.python: categoriesMock, - SDK.go: categoriesMock, - SDK.scio: categoriesMock, -}; diff --git a/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart b/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart deleted file mode 100644 index 8215315d7fc8..000000000000 --- a/playground/frontend/test/pages/playground/states/mocks/example_repository_mock.mocks.dart +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Mocks generated by Mockito 5.2.0 from annotations -// in playground/test/pages/playground/states/mocks/example_repository_mock.dart. -// Do not manually edit this file. - -import 'dart:async' as _i5; - -import 'package:mockito/mockito.dart' as _i1; -import 'package:playground/modules/examples/models/category_model.dart' as _i7; -import 'package:playground/modules/examples/models/example_model.dart' as _i2; -import 'package:playground/modules/examples/repositories/example_repository.dart' - as _i4; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart' - as _i9; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart' - as _i8; -import 'package:playground/modules/examples/repositories/models/get_snippet_request.dart' - as _i10; -import 'package:playground/modules/examples/repositories/models/get_snippet_response.dart' - as _i3; -import 'package:playground/modules/examples/repositories/models/save_snippet_request.dart' - as _i11; -import 'package:playground/modules/sdk/models/sdk.dart' as _i6; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types - -class _FakeExampleModel_0 extends _i1.Fake implements _i2.ExampleModel {} - -class _FakeGetSnippetResponse_1 extends _i1.Fake - implements _i3.GetSnippetResponse {} - -/// A class which mocks [ExampleRepository]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockExampleRepository extends _i1.Mock implements _i4.ExampleRepository { - MockExampleRepository() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.Future>> getListOfExamples( - _i8.GetListOfExamplesRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getListOfExamples, [request]), - returnValue: Future>>.value( - <_i6.SDK, List<_i7.CategoryModel>>{})) - as _i5.Future>>); - @override - _i5.Future<_i2.ExampleModel> getDefaultExample( - _i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getDefaultExample, [request]), - returnValue: - Future<_i2.ExampleModel>.value(_FakeExampleModel_0())) - as _i5.Future<_i2.ExampleModel>); - @override - _i5.Future getExampleSource(_i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleSource, [request]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future getExampleOutput(_i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleOutput, [request]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future getExampleLogs(_i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleLogs, [request]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future getExampleGraph(_i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExampleGraph, [request]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future<_i2.ExampleModel> getExample( - _i9.GetExampleRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getExample, [request]), - returnValue: - Future<_i2.ExampleModel>.value(_FakeExampleModel_0())) - as _i5.Future<_i2.ExampleModel>); - @override - _i5.Future<_i3.GetSnippetResponse> getSnippet( - _i10.GetSnippetRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#getSnippet, [request]), - returnValue: Future<_i3.GetSnippetResponse>.value( - _FakeGetSnippetResponse_1())) - as _i5.Future<_i3.GetSnippetResponse>); - @override - _i5.Future saveSnippet(_i11.SaveSnippetRequestWrapper? request) => - (super.noSuchMethod(Invocation.method(#saveSnippet, [request]), - returnValue: Future.value('')) as _i5.Future); -} diff --git a/playground/frontend/test/pages/playground/states/mocks/request_mock.dart b/playground/frontend/test/pages/playground/states/mocks/request_mock.dart deleted file mode 100644 index b08a81ebd55d..000000000000 --- a/playground/frontend/test/pages/playground/states/mocks/request_mock.dart +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_code_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_example_response.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_request.dart'; -import 'package:playground/modules/examples/repositories/models/get_list_of_examples_response.dart'; -import 'package:playground/modules/sdk/models/sdk.dart'; - -import 'categories_mock.dart'; -import 'example_mock.dart'; - -final kGetListOfExamplesRequestMock = - GetListOfExamplesRequestWrapper(sdk: null, category: null); -final kGetListOfExamplesResponseMock = - GetListOfExampleResponse(sdkCategoriesFromServerMock); -final kGetExampleRequestMock = GetExampleRequestWrapper('', SDK.java); -final kGetExampleResponseMock = GetExampleResponse(exampleMock1); -final kGetExampleCodeResponseMock = GetExampleCodeResponse('test source'); -final kOutputResponseMock = OutputResponse('test outputs'); - -final kRequestForExampleInfo = - GetExampleRequestWrapper('SDK_PYTHON/Category/Name', SDK.python); -final kRequestDefaultExampleForJava = GetExampleRequestWrapper('', SDK.java); -final kRequestDefaultExampleForGo = GetExampleRequestWrapper('', SDK.go); -final kRequestDefaultExampleForPython = - GetExampleRequestWrapper('', SDK.python); -final kRequestDefaultExampleForScio = GetExampleRequestWrapper('', SDK.scio); diff --git a/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart b/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart deleted file mode 100644 index e8c4b07d0d1e..000000000000 --- a/playground/frontend/test/pages/playground/states/playground_state_test.mocks.dart +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Mocks generated by Mockito 5.2.0 from annotations -// in playground/test/pages/playground/states/playground_state_test.dart. -// Do not manually edit this file. - -import 'dart:async' as _i5; -import 'dart:ui' as _i12; - -import 'package:mockito/mockito.dart' as _i1; -import 'package:playground/modules/examples/models/category_model.dart' as _i10; -import 'package:playground/modules/examples/models/example_loading_descriptors/example_loading_descriptor.dart' - as _i7; -import 'package:playground/modules/examples/models/example_loading_descriptors/examples_loading_descriptor.dart' - as _i6; -import 'package:playground/modules/examples/models/example_model.dart' as _i2; -import 'package:playground/modules/examples/repositories/models/shared_file_model.dart' - as _i11; -import 'package:playground/modules/sdk/models/sdk.dart' as _i9; -import 'package:playground/pages/playground/states/example_loaders/examples_loader.dart' - as _i3; -import 'package:playground/pages/playground/states/examples_state.dart' as _i8; -import 'package:playground/pages/playground/states/playground_state.dart' - as _i4; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types - -class _FakeExampleModel_0 extends _i1.Fake implements _i2.ExampleModel {} - -/// A class which mocks [ExamplesLoader]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockExamplesLoader extends _i1.Mock implements _i3.ExamplesLoader { - MockExamplesLoader() { - _i1.throwOnMissingStub(this); - } - - @override - void setPlaygroundState(_i4.PlaygroundState? value) => - super.noSuchMethod(Invocation.method(#setPlaygroundState, [value]), - returnValueForMissingStub: null); - @override - _i5.Future load(_i6.ExamplesLoadingDescriptor? descriptor) => - (super.noSuchMethod(Invocation.method(#load, [descriptor]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadOne( - {_i6.ExamplesLoadingDescriptor? group, - _i7.ExampleLoadingDescriptor? one}) => - (super.noSuchMethod( - Invocation.method(#loadOne, [], {#group: group, #one: one}), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); -} - -/// A class which mocks [ExampleState]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockExampleState extends _i1.Mock implements _i8.ExampleState { - MockExampleState() { - _i1.throwOnMissingStub(this); - } - - @override - set sdkCategories(Map<_i9.SDK, List<_i10.CategoryModel>>? _sdkCategories) => - super.noSuchMethod(Invocation.setter(#sdkCategories, _sdkCategories), - returnValueForMissingStub: null); - @override - Map<_i9.SDK, _i2.ExampleModel> get defaultExamplesMap => - (super.noSuchMethod(Invocation.getter(#defaultExamplesMap), - returnValue: <_i9.SDK, _i2.ExampleModel>{}) - as Map<_i9.SDK, _i2.ExampleModel>); - @override - set defaultExamplesMap(Map<_i9.SDK, _i2.ExampleModel>? _defaultExamplesMap) => - super.noSuchMethod( - Invocation.setter(#defaultExamplesMap, _defaultExamplesMap), - returnValueForMissingStub: null); - @override - set defaultExample(_i2.ExampleModel? _defaultExample) => - super.noSuchMethod(Invocation.setter(#defaultExample, _defaultExample), - returnValueForMissingStub: null); - @override - bool get isSelectorOpened => - (super.noSuchMethod(Invocation.getter(#isSelectorOpened), - returnValue: false) as bool); - @override - set isSelectorOpened(bool? _isSelectorOpened) => super.noSuchMethod( - Invocation.setter(#isSelectorOpened, _isSelectorOpened), - returnValueForMissingStub: null); - @override - _i5.Future get allExamplesFuture => - (super.noSuchMethod(Invocation.getter(#allExamplesFuture), - returnValue: Future.value()) as _i5.Future); - @override - bool get hasExampleCatalog => - (super.noSuchMethod(Invocation.getter(#hasExampleCatalog), - returnValue: false) as bool); - @override - bool get hasListeners => - (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) - as bool); - @override - _i5.Future init() => (super.noSuchMethod(Invocation.method(#init, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - void setSdkCategories(Map<_i9.SDK, List<_i10.CategoryModel>>? map) => - super.noSuchMethod(Invocation.method(#setSdkCategories, [map]), - returnValueForMissingStub: null); - @override - List<_i10.CategoryModel> getCategories(_i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getCategories, [sdk]), - returnValue: <_i10.CategoryModel>[]) as List<_i10.CategoryModel>); - @override - _i5.Future getExampleOutput(String? id, _i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getExampleOutput, [id, sdk]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future getExampleSource(String? id, _i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getExampleSource, [id, sdk]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future<_i2.ExampleModel> getExample(String? path, _i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getExample, [path, sdk]), - returnValue: - Future<_i2.ExampleModel>.value(_FakeExampleModel_0())) - as _i5.Future<_i2.ExampleModel>); - @override - _i5.Future getExampleLogs(String? id, _i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getExampleLogs, [id, sdk]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future getExampleGraph(String? id, _i9.SDK? sdk) => - (super.noSuchMethod(Invocation.method(#getExampleGraph, [id, sdk]), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future<_i2.ExampleModel> loadSharedExample(String? id) => - (super.noSuchMethod(Invocation.method(#loadSharedExample, [id]), - returnValue: - Future<_i2.ExampleModel>.value(_FakeExampleModel_0())) - as _i5.Future<_i2.ExampleModel>); - @override - _i5.Future getSnippetId( - {List<_i11.SharedFile>? files, - _i9.SDK? sdk, - String? pipelineOptions}) => - (super.noSuchMethod( - Invocation.method(#getSnippetId, [], - {#files: files, #sdk: sdk, #pipelineOptions: pipelineOptions}), - returnValue: Future.value('')) as _i5.Future); - @override - _i5.Future<_i2.ExampleModel> loadExampleInfo(_i2.ExampleModel? example) => - (super.noSuchMethod(Invocation.method(#loadExampleInfo, [example]), - returnValue: - Future<_i2.ExampleModel>.value(_FakeExampleModel_0())) - as _i5.Future<_i2.ExampleModel>); - @override - void changeSelectorVisibility() => - super.noSuchMethod(Invocation.method(#changeSelectorVisibility, []), - returnValueForMissingStub: null); - @override - _i5.Future loadDefaultExamples() => - (super.noSuchMethod(Invocation.method(#loadDefaultExamples, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadDefaultExamplesIfNot() => - (super.noSuchMethod(Invocation.method(#loadDefaultExamplesIfNot, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future<_i2.ExampleModel?> getCatalogExampleByPath(String? path) => - (super.noSuchMethod(Invocation.method(#getCatalogExampleByPath, [path]), - returnValue: Future<_i2.ExampleModel?>.value()) - as _i5.Future<_i2.ExampleModel?>); - @override - void addListener(_i12.VoidCallback? listener) => - super.noSuchMethod(Invocation.method(#addListener, [listener]), - returnValueForMissingStub: null); - @override - void removeListener(_i12.VoidCallback? listener) => - super.noSuchMethod(Invocation.method(#removeListener, [listener]), - returnValueForMissingStub: null); - @override - void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), - returnValueForMissingStub: null); - @override - void notifyListeners() => - super.noSuchMethod(Invocation.method(#notifyListeners, []), - returnValueForMissingStub: null); -}