Line data Source code
1 : import 'dart:collection';
2 :
3 : import 'package:flutter/material.dart';
4 : import 'package:widgetbook/src/models/models.dart';
5 : import 'package:widgetbook/src/models/organizers/organizer_helper/organizer_helper.dart';
6 : import 'package:widgetbook/src/providers/organizer_state.dart';
7 : import 'package:widgetbook/src/providers/provider.dart';
8 : import 'package:widgetbook/src/repositories/selected_story_repository.dart';
9 : import 'package:widgetbook/src/repositories/story_repository.dart';
10 :
11 : class OrganizerBuilder extends StatefulWidget {
12 : final Widget child;
13 : final List<Category> categories;
14 : final StoryRepository storyRepository;
15 : final SelectedStoryRepository selectedStoryRepository;
16 :
17 1 : const OrganizerBuilder({
18 : Key? key,
19 : required this.child,
20 : required this.categories,
21 : required this.storyRepository,
22 : required this.selectedStoryRepository,
23 1 : }) : super(key: key);
24 :
25 1 : @override
26 1 : _OrganizerBuilderState createState() => _OrganizerBuilderState();
27 : }
28 :
29 : class _OrganizerBuilderState extends State<OrganizerBuilder> {
30 : late OrganizerState state;
31 : late OrganizerProvider provider;
32 1 : @override
33 : void initState() {
34 2 : state = OrganizerState.unfiltered(
35 2 : categories: widget.categories,
36 : );
37 1 : setProvider();
38 :
39 5 : widget.selectedStoryRepository.getStream().forEach((Story? story) {
40 2 : provider.openStory(story);
41 : });
42 :
43 1 : super.initState();
44 : }
45 :
46 1 : void setProvider() {
47 2 : provider = OrganizerProvider(
48 2 : selectedStoryRepository: widget.selectedStoryRepository,
49 2 : storyRepository: widget.storyRepository,
50 1 : state: state,
51 1 : onStateChanged: (OrganizerState state) {
52 2 : setState(() {
53 1 : this.state = state;
54 1 : setProvider();
55 : });
56 : },
57 2 : child: widget.child,
58 : );
59 : }
60 :
61 1 : @override
62 : Widget build(BuildContext context) {
63 1 : return provider;
64 : }
65 : }
66 :
67 : class OrganizerProvider extends Provider<OrganizerState> {
68 : final SelectedStoryRepository selectedStoryRepository;
69 : final StoryRepository storyRepository;
70 :
71 1 : const OrganizerProvider({
72 : required this.selectedStoryRepository,
73 : required this.storyRepository,
74 : required OrganizerState state,
75 : required ValueChanged<OrganizerState> onStateChanged,
76 : required Widget child,
77 : Key? key,
78 1 : }) : super(
79 : state: state,
80 : onStateChanged: onStateChanged,
81 : child: child,
82 : key: key,
83 : );
84 :
85 1 : static OrganizerProvider? of(BuildContext context) {
86 1 : return context.dependOnInheritedWidgetOfExactType<OrganizerProvider>();
87 : }
88 :
89 1 : void openStory(Story? story) {
90 : if (story == null) {
91 : return;
92 : }
93 : ExpandableOrganizer? currentOrganizer =
94 1 : story.parent as ExpandableOrganizer?;
95 : while (currentOrganizer != null) {
96 1 : currentOrganizer.isExpanded = true;
97 1 : currentOrganizer = currentOrganizer.parent as ExpandableOrganizer?;
98 : }
99 : }
100 :
101 1 : void _updateFolders(List<Category> categories) {
102 1 : var oldFolders = FolderHelper.getAllFoldersFromCategories(
103 2 : state.allCategories,
104 : );
105 1 : var newFolders = FolderHelper.getAllFoldersFromCategories(
106 : categories,
107 : );
108 1 : var oldFolderMap = HashMap<String, Folder>.fromIterable(
109 : oldFolders,
110 2 : key: (k) => k.path,
111 1 : value: (v) => v,
112 : );
113 :
114 2 : for (var folder in newFolders) {
115 1 : var path = folder.path;
116 1 : if (oldFolderMap.containsKey(path)) {
117 3 : folder.isExpanded = oldFolderMap[path]!.isExpanded;
118 : }
119 : }
120 : }
121 :
122 1 : void _updateWidgets(List<Category> categories) {
123 1 : var oldWidgets = WidgetHelper.getAllWidgetElementsFromCategories(
124 2 : state.allCategories,
125 : );
126 1 : var newWidgets = WidgetHelper.getAllWidgetElementsFromCategories(
127 : categories,
128 : );
129 1 : var oldFolderMap = HashMap<String, WidgetElement>.fromIterable(
130 : oldWidgets,
131 2 : key: (k) => k.path,
132 1 : value: (v) => v,
133 : );
134 :
135 2 : for (var widget in newWidgets) {
136 1 : var path = widget.path;
137 1 : if (oldFolderMap.containsKey(path)) {
138 3 : widget.isExpanded = oldFolderMap[path]!.isExpanded;
139 : }
140 : }
141 : }
142 :
143 1 : void update(List<Category> categories) {
144 1 : _updateFolders(categories);
145 1 : _updateWidgets(categories);
146 1 : emit(
147 1 : OrganizerState.unfiltered(categories: categories),
148 : );
149 :
150 1 : var stories = StoryHelper.getAllStoriesFromCategories(categories);
151 2 : storyRepository.deleteAll();
152 2 : storyRepository.addAll(stories);
153 : }
154 :
155 : // The filter methods are used to implement a search of organizer elements
156 : // since no UI element and no tests exist, this functionality
157 : // is currently disabled
158 : //
159 : // void resetFilter() {
160 : // emit(
161 : // OrganizerState.unfiltered(
162 : // categories: state.allCategories,
163 : // ),
164 : // );
165 : // }
166 :
167 : // void filter(RegExp regExp) {
168 : // var categories = _filterCategories(
169 : // regExp,
170 : // state.allCategories,
171 : // );
172 :
173 : // emit(
174 : // OrganizerState(
175 : // allCategories: state.allCategories,
176 : // filteredCategories: categories,
177 : // searchTerm: regExp.pattern,
178 : // ),
179 : // );
180 : // }
181 :
182 : // List<Category> _filterCategories(
183 : // RegExp regExp,
184 : // List<Category> categories,
185 : // ) {
186 : // List<Category> matchingOrganizers = <Category>[];
187 : // for (var category in categories) {
188 : // Category? result = _filterOrganizer(regExp, category) as Category?;
189 : // if (_isMatch(result)) {
190 : // matchingOrganizers.add(result!);
191 : // }
192 : // }
193 : // return matchingOrganizers;
194 : // }
195 :
196 : // ExpandableOrganizer? _filterOrganizer(
197 : // RegExp regExp, ExpandableOrganizer organizer) {
198 : // if (organizer.name.contains(regExp)) {
199 : // return organizer;
200 : // }
201 :
202 : // List<Folder> matchingFolders = <Folder>[];
203 : // for (var subOrganizer in organizer.folders) {
204 : // ExpandableOrganizer? result = _filterOrganizer(regExp, subOrganizer);
205 : // if (_isMatch(result)) {
206 : // matchingFolders.add(result! as Folder);
207 : // }
208 : // }
209 :
210 : // List<WidgetElement> matchingWidgets = <WidgetElement>[];
211 : // for (var subOrganizer in organizer.widgets) {
212 : // ExpandableOrganizer? result = _filterOrganizer(regExp, subOrganizer);
213 : // if (_isMatch(result)) {
214 : // matchingWidgets.add(result! as WidgetElement);
215 : // }
216 : // }
217 :
218 : // if (matchingFolders.isNotEmpty) {
219 : // return _createFilteredSubtree(
220 : // organizer,
221 : // matchingFolders,
222 : // matchingWidgets,
223 : // );
224 : // }
225 :
226 : // return null;
227 : // }
228 :
229 : // ExpandableOrganizer _createFilteredSubtree(
230 : // ExpandableOrganizer organizer,
231 : // List<Folder> folders,
232 : // List<WidgetElement> widgets,
233 : // ) {
234 : // if (organizer is Category) {
235 : // return Category(
236 : // name: organizer.name,
237 : // widgets: widgets,
238 : // folders: folders,
239 : // );
240 : // }
241 : // if (organizer is Folder) {
242 : // return Folder(
243 : // name: organizer.name,
244 : // widgets: widgets,
245 : // folders: folders,
246 : // );
247 : // } else {
248 : // // TODO remove this when tested
249 : // // ignore: avoid_print
250 : // print('This message should never appear - BUG!');
251 : // return Folder(name: 'If you see this, you have found a bug');
252 : // }
253 : // }
254 :
255 : // bool _isMatch(ExpandableOrganizer? organizer) {
256 : // return organizer != null;
257 : // }
258 :
259 1 : void toggleExpander(ExpandableOrganizer organizer) {
260 2 : organizer.isExpanded = !organizer.isExpanded;
261 1 : emit(
262 1 : OrganizerState(
263 2 : allCategories: state.allCategories,
264 2 : filteredCategories: state.filteredCategories,
265 2 : searchTerm: state.searchTerm,
266 : ),
267 : );
268 : }
269 : }
|