diff --git a/learning/tour-of-beam/frontend/lib/models/group.dart b/learning/tour-of-beam/frontend/lib/models/group.dart index 22086e6303e8..ba1d4047a574 100644 --- a/learning/tour-of-beam/frontend/lib/models/group.dart +++ b/learning/tour-of-beam/frontend/lib/models/group.dart @@ -23,15 +23,28 @@ import 'parent_node.dart'; class GroupModel extends ParentNodeModel { const GroupModel({ required super.id, - required super.title, required super.nodes, + required super.parent, + required super.title, }); - GroupModel.fromResponse(GroupResponseModel group) - : super( - id: group.id, - title: group.title, - nodes: - group.nodes.map(NodeModel.fromResponse).toList(growable: false), - ); + factory GroupModel.fromResponse( + GroupResponseModel groupResponse, + ParentNodeModel parent, + ) { + final group = GroupModel( + id: groupResponse.id, + nodes: [], + parent: parent, + title: groupResponse.title, + ); + + group.nodes.addAll( + groupResponse.nodes.map( + (node) => NodeModel.fromResponse(node, group), + ), + ); + + return group; + } } diff --git a/learning/tour-of-beam/frontend/lib/models/module.dart b/learning/tour-of-beam/frontend/lib/models/module.dart index 81f8c1b6d613..eb1f7e50633c 100644 --- a/learning/tour-of-beam/frontend/lib/models/module.dart +++ b/learning/tour-of-beam/frontend/lib/models/module.dart @@ -27,18 +27,27 @@ class ModuleModel extends ParentNodeModel { const ModuleModel({ required super.id, - required super.title, required super.nodes, + required super.parent, + required super.title, required this.complexity, }); - ModuleModel.fromResponse(ModuleResponseModel module) - : complexity = module.complexity, - super( - id: module.id, - title: module.title, - nodes: module.nodes - .map(NodeModel.fromResponse) - .toList(growable: false), - ); + factory ModuleModel.fromResponse(ModuleResponseModel moduleResponse) { + final module = ModuleModel( + complexity: moduleResponse.complexity, + nodes: [], + id: moduleResponse.id, + parent: null, + title: moduleResponse.title, + ); + + module.nodes.addAll( + moduleResponse.nodes.map( + (node) => NodeModel.fromResponse(node, module), + ), + ); + + return module; + } } diff --git a/learning/tour-of-beam/frontend/lib/models/node.dart b/learning/tour-of-beam/frontend/lib/models/node.dart index d13ceea2d282..df58667a48c8 100644 --- a/learning/tour-of-beam/frontend/lib/models/node.dart +++ b/learning/tour-of-beam/frontend/lib/models/node.dart @@ -19,15 +19,18 @@ import '../repositories/models/node.dart'; import '../repositories/models/node_type_enum.dart'; import 'group.dart'; +import 'parent_node.dart'; import 'unit.dart'; abstract class NodeModel { final String id; final String title; + final NodeModel? parent; const NodeModel({ required this.id, required this.title, + required this.parent, }); /// Constructs nodes from the response data. @@ -36,20 +39,23 @@ abstract class NodeModel { /// because they come from a golang backend which does not /// support inheritance, and so they use an extra layer of composition /// which is inconvenient in Flutter. - static List fromMaps(List json) { + static List fromMaps(List json, ParentNodeModel parent) { return json .cast>() .map(NodeResponseModel.fromJson) - .map(fromResponse) + .map((nodeResponse) => fromResponse(nodeResponse, parent)) .toList(); } - static NodeModel fromResponse(NodeResponseModel node) { + static NodeModel fromResponse( + NodeResponseModel node, + ParentNodeModel parent, + ) { switch (node.type) { case NodeType.group: - return GroupModel.fromResponse(node.group!); + return GroupModel.fromResponse(node.group!, parent); case NodeType.unit: - return UnitModel.fromResponse(node.unit!); + return UnitModel.fromResponse(node.unit!, parent); } } } diff --git a/learning/tour-of-beam/frontend/lib/models/parent_node.dart b/learning/tour-of-beam/frontend/lib/models/parent_node.dart index 0271cfb9508f..d7ec71bd1886 100644 --- a/learning/tour-of-beam/frontend/lib/models/parent_node.dart +++ b/learning/tour-of-beam/frontend/lib/models/parent_node.dart @@ -23,6 +23,7 @@ abstract class ParentNodeModel extends NodeModel { const ParentNodeModel({ required super.id, + required super.parent, required super.title, required this.nodes, }); diff --git a/learning/tour-of-beam/frontend/lib/models/unit.dart b/learning/tour-of-beam/frontend/lib/models/unit.dart index 48b55af33d15..52b34072953e 100644 --- a/learning/tour-of-beam/frontend/lib/models/unit.dart +++ b/learning/tour-of-beam/frontend/lib/models/unit.dart @@ -18,8 +18,13 @@ import '../repositories/models/unit.dart'; import 'node.dart'; +import 'parent_node.dart'; class UnitModel extends NodeModel { - UnitModel.fromResponse(UnitResponseModel unit) - : super(id: unit.id, title: unit.title); + UnitModel.fromResponse(UnitResponseModel unit, ParentNodeModel parent) + : super( + id: unit.id, + parent: parent, + title: unit.title, + ); } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart b/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart index dc5fc5a15ceb..9952cbbaecdb 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart @@ -43,7 +43,16 @@ class ContentTreeController extends ChangeNotifier { } _currentNode = node; - // TODO(alexeyinkin): Set _treeIds from node. + _treeIds = _getNodeAncestors(node, [node.id]); notifyListeners(); } + + List _getNodeAncestors(NodeModel node, List ancestors) { + if (node.parent != null) { + ancestors.add(node.parent!.id); + return _getNodeAncestors(node.parent!, ancestors); + } else { + return ancestors.reversed.toList(); + } + } } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/state.dart b/learning/tour-of-beam/frontend/lib/pages/tour/state.dart index ae8fc0e1e706..e4a0c4df2243 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/state.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/state.dart @@ -53,6 +53,7 @@ class TourNotifier extends ChangeNotifier with PageStateMixin { ); void _onChanged() { + emitPathChanged(); final currentNode = contentTreeController.currentNode; if (currentNode is UnitModel) { final content = _unitContentCache.getUnitContent(