Skip to content

Commit

Permalink
feat(cat-voices): workspace only edit one step at the time (#1219)
Browse files Browse the repository at this point in the history
* feat: worksapce only edit one step at the time

* chore: remove unused property

* feat: select step when editing

* refactor: remove unsed constructor

* refactor: rename function

---------

Co-authored-by: Oleksandr Prokhorenko <[email protected]>
  • Loading branch information
damian-molinski and minikin authored Nov 15, 2024
1 parent 71b6915 commit ca1c4f9
Show file tree
Hide file tree
Showing 7 changed files with 479 additions and 170 deletions.
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 ca1c4f9

Please sign in to comment.