Skip to content

Commit

Permalink
Merge branch 'main' into feat/category_menu_1183
Browse files Browse the repository at this point in the history
  • Loading branch information
dtscalac authored Nov 18, 2024
2 parents 43f36ff + 31ad353 commit 4b0070a
Show file tree
Hide file tree
Showing 12 changed files with 535 additions and 177 deletions.
6 changes: 5 additions & 1 deletion catalyst_voices/.gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
### Dart ###
# See https://www.dartlang.org/guides/libraries/private-files


# Generated files from code generation tools
*.g.dart
*.freezed.dart
*.chopper.dart
*.swagger.dart
*.openapi.dart
*.gen.dart
*.swagger.*.dart

# Un-ignore generated files in public packages
!**/packages/libs/**/*.g.dart
Expand Down Expand Up @@ -149,4 +151,6 @@ coverage/
**/fastlane/test_output

# Fastlane.swift runner binary
**/fastlane/FastlaneRunner
**/fastlane/FastlaneRunner

devtools_options.yaml
33 changes: 29 additions & 4 deletions catalyst_voices/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,32 @@ builder:
COPY +repo-catalyst-voices/repo .
DO flutter-ci+BOOTSTRAP

# Creates filtered OpenAPI spec
# Takes json file from openapi-filter from /packages/internal/catalyst_voices_services
filter-openapi:
FROM node:18
WORKDIR /packages/internal/catalyst_voices_services

COPY catalyst-gateway+build/doc/cat-gateway-api.json openapi/cat-gateway-api.json
COPY packages/internal/catalyst_voices_services/openapi-filters.json openapi-filters.json

RUN npm install -g openapi-format
RUN openapi-format openapi/cat-gateway-api.json -o openapi/filtered-openapi.json --filterFile openapi-filters.json --verbose

RUN rm openapi/cat-gateway-api.json

SAVE ARTIFACT openapi/filtered-openapi.json

# Generates flutter code.
# Generates codes for Catalyst Gateway OpenAPI, Voices Localization and
# VoicesAssets and other packages that depend on code-generator.
# It accepts [save_locally] ARG that when true place the artifacts in the
# proper folders
# It accepts [filter_openapi] ARG that when true filter the openapi spec
# using filters from /packages/internal/catalyst_voices_services/openapi-filters.json
code-generator:
ARG save_locally=false
ARG save_locally=false
ARG filter_openapi=true
FROM +builder
LET gen_code_path = lib/generated/catalyst_gateway
LET local_gen_code_path = packages/internal/catalyst_voices_services/lib/generated/catalyst_gateway/
Expand All @@ -33,16 +52,22 @@ code-generator:
RUN melos build_runner

IF [ $save_locally = true ]
RUN find . \( -name "*.g.dart" -o -name "*.freezed.dart" -o -name "*.chopper.dart" -o -name "*.swagger.dart" -o -name "*.openapi.dart" -o -name "*.gen.dart" -o -name "catalyst_voices_localizations*.dart" \)
RUN find . \( -name "*.g.dart" -o -name "*.freezed.dart" -o -name "*.chopper.dart" -o -name "*.swagger.dart" -o -name "*.openapi.dart" -o -name "*.gen.dart" -o -name "catalyst_voices_localizations*.dart" -o -name "cat_gateway_api.*.swagger.*" \)

FOR generated_file IN $(find . \( -name "*.g.dart" -o -name "*.freezed.dart" -o -name "*.chopper.dart" -o -name "*.swagger.dart" -o -name "*.openapi.dart" -o -name "*.gen.dart" -o -name "catalyst_voices_localizations*.dart" \))
FOR generated_file IN $(find . \( -name "*.g.dart" -o -name "*.freezed.dart" -o -name "*.chopper.dart" -o -name "*.swagger.dart" -o -name "*.openapi.dart" -o -name "*.gen.dart" -o -name "catalyst_voices_localizations*.dart" -o -name "cat_gateway_api.*.swagger.*" \))
SAVE ARTIFACT $generated_file AS LOCAL $generated_file
END
ELSE
SAVE ARTIFACT .
END
WORKDIR packages/internal/catalyst_voices_services
COPY catalyst-gateway+build/doc/cat-gateway-api.json openapi/cat-gateway-api.json

IF [ $filter_openapi = true ]
COPY +filter-openapi/filtered-openapi.json openapi/cat-gateway-api.json
ELSE
COPY catalyst-gateway+build/doc/cat-gateway-api.json openapi/cat-gateway-api.json
END

DO flutter-ci+OPENAPI_CODE_GEN \
--SAVE_LOCALLY=$save_locally \
--GEN_CODE_PATH=$gen_code_path \
Expand Down
5 changes: 4 additions & 1 deletion catalyst_voices/apps/voices/integration_test/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ integration-test-web:
exit 1
END

test-web-all:
# TODO(dtscalac): disabled integration tests due to them being flaky,
# reenable when enable-threads.js workaround is removed
# and https://github.com/fzyzcjy/flutter_rust_bridge/issues/2407 closed
disabled-test-web-all:
BUILD +integration-test-web \
--browser=chrome \
--browser=firefox
Original file line number Diff line number Diff line change
@@ -1,33 +1,102 @@
import 'package:catalyst_voices/widgets/navigation/section_step_state_builder.dart';
import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';

class WorkspaceRichTextStep extends StatelessWidget {
class WorkspaceRichTextStep extends StatefulWidget {
final RichTextStep step;

const WorkspaceRichTextStep({
super.key,
required this.step,
});

@override
State<WorkspaceRichTextStep> createState() => _WorkspaceRichTextStepState();
}

class _WorkspaceRichTextStepState extends State<WorkspaceRichTextStep> {
late final VoicesRichTextController _controller;
late final VoicesRichTextEditModeController _editModeController;

@override
void initState() {
super.initState();

final document = Document.fromJson(widget.step.data.value);
final selectionOffset = document.length == 0 ? 0 : document.length - 1;

_controller = VoicesRichTextController(
document: document,
selection: TextSelection.collapsed(offset: selectionOffset),
);
_editModeController = VoicesRichTextEditModeController();
_editModeController.addListener(_onEditModeControllerChanged);
}

@override
void dispose() {
_editModeController.dispose();
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return SectionStepStateBuilder(
id: step.sectionStepId,
id: widget.step.sectionStepId,
builder: (context, value, child) {
return WorkspaceTileContainer(
isSelected: value.isSelected,
content: child!,
);
},
child: VoicesRichText(
title: step.localizedDesc(context),
document: Document.fromJson(step.data.value),
charsLimit: step.charsLimit,
title: widget.step.localizedDesc(context),
controller: _controller,
editModeController: _editModeController,
charsLimit: widget.step.charsLimit,
canEditDocumentGetter: _canEditDocument,
onEditBlocked: _showEditBlockedRationale,
),
);
}

bool _canEditDocument(Document document) {
final sectionsController = SectionsControllerScope.of(context);

final ids = sectionsController.value.editStepsIds;
final isEditing = ids.isNotEmpty;

return !isEditing;
}

Future<void> _showEditBlockedRationale() async {
await VoicesDialog.show<void>(
context: context,
builder: (context) {
return VoicesAlertDialog(
title: Text(context.l10n.warning),
subtitle: Text(context.l10n.saveBeforeEditingErrorText),
buttons: [
VoicesFilledButton(
child: Text(context.l10n.ok),
onTap: () => Navigator.of(context).pop(),
),
],
);
},
);
}

void _onEditModeControllerChanged() {
final isEditMode = _editModeController.value;
final sectionsController = SectionsControllerScope.of(context);
final id = widget.step.sectionStepId;

sectionsController.editStep(id, enabled: isEditMode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,13 @@ final class SectionsControllerState extends Equatable {
final List<Section> sections;
final Set<int> openedSections;
final SectionStepId? activeStepId;
final Set<SectionStepId> editStepsIds;

factory SectionsControllerState({
List<Section> sections = const [],
Set<int> openedSections = const {},
SectionStepId? activeStepId,
}) {
return SectionsControllerState._(
sections: sections,
openedSections: openedSections,
activeStepId: activeStepId,
);
}

const SectionsControllerState._({
const SectionsControllerState({
this.sections = const [],
this.openedSections = const {},
this.activeStepId,
this.editStepsIds = const {},
});

int? get activeSectionId => activeStepId?.sectionId;
Expand Down Expand Up @@ -70,28 +60,30 @@ final class SectionsControllerState extends Equatable {
List<Section>? sections,
Set<int>? openedSections,
Optional<SectionStepId>? activeStepId,
Set<SectionStepId>? editStepsIds,
}) {
return SectionsControllerState(
sections: sections ?? this.sections,
openedSections: openedSections ?? this.openedSections,
activeStepId: activeStepId.dataOr(this.activeStepId),
editStepsIds: editStepsIds ?? this.editStepsIds,
);
}

@override
List<Object?> get props => [
sections,
listItems,
openedSections,
activeStepId,
editStepsIds,
];
}

final class SectionsController extends ValueNotifier<SectionsControllerState> {
ItemScrollController? _itemsScrollController;

SectionsController([
super.value = const SectionsControllerState._(),
super.value = const SectionsControllerState(),
]) : super();

// ignore: use_setters_to_change_properties
Expand Down Expand Up @@ -145,6 +137,26 @@ final class SectionsController extends ValueNotifier<SectionsControllerState> {
unawaited(_scrollToSection(id));
}

void editStep(
SectionStepId id, {
required bool enabled,
}) {
final editStepsIds = <SectionStepId>{...value.editStepsIds};
Optional<SectionStepId>? activeStepId;

if (enabled) {
editStepsIds.add(id);
activeStepId = Optional.of(id);
} else {
editStepsIds.remove(id);
}

value = value.copyWith(
editStepsIds: editStepsIds,
activeStepId: activeStepId,
);
}

@override
void dispose() {
detachItemsScrollController();
Expand Down
Loading

0 comments on commit 4b0070a

Please sign in to comment.