| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * The top-level class for the UI state. UI state is essentially a "model" from | |
| 7 * the view's perspective but whose data just describes the UI itself. It | |
| 8 * contains data like the currently selected story, etc. | |
| 9 */ | |
| 10 // TODO(jimhug): Split the two classes here into framework and app-specific. | |
| 11 class SwarmState extends UIState { | |
| 12 /** Core data source for the app. */ | |
| 13 final Sections _dataModel; | |
| 14 | |
| 15 /** | |
| 16 * Which article the user is currently viewing, or null if they aren't | |
| 17 * viewing an Article. | |
| 18 */ | |
| 19 final ObservableValue<Article> currentArticle; | |
| 20 /** | |
| 21 * Which article the user currently has selected (for traversing articles | |
| 22 * via keyboard shortcuts). | |
| 23 */ | |
| 24 final ObservableValue<Article> selectedArticle; | |
| 25 | |
| 26 /** | |
| 27 * True if the story view is maximized and the top and bottom UI elements | |
| 28 * are hidden. | |
| 29 */ | |
| 30 final ObservableValue<bool> storyMaximized; | |
| 31 | |
| 32 /** | |
| 33 * True if the maximized story, if any, is being displayed in text mode | |
| 34 * rather than as an embedded web-page. | |
| 35 */ | |
| 36 final ObservableValue<bool> storyTextMode; | |
| 37 | |
| 38 /** | |
| 39 * Which article the user currently has selected (by keyboard shortcuts), | |
| 40 * or null if an article isn't selected by the keyboard. | |
| 41 */ | |
| 42 BiIterator<Article> _articleIterator; | |
| 43 | |
| 44 /** | |
| 45 * Which feed is currently selected (for keyboard shortcuts). | |
| 46 */ | |
| 47 BiIterator<Feed> _feedIterator; | |
| 48 | |
| 49 /** | |
| 50 * Which section is currently selected (for keyboard shortcuts). | |
| 51 */ | |
| 52 BiIterator<Section> _sectionIterator; | |
| 53 | |
| 54 SwarmState(this._dataModel) | |
| 55 : super(), | |
| 56 currentArticle = new ObservableValue<Article>(null), | |
| 57 selectedArticle = new ObservableValue<Article>(null), | |
| 58 storyMaximized = new ObservableValue<bool>(false), | |
| 59 storyTextMode = new ObservableValue<bool>(true) { | |
| 60 startHistoryTracking(); | |
| 61 // TODO(efortuna): consider having this class just hold observable | |
| 62 // currentIndecies instead of iterators with observablevalues.. | |
| 63 _sectionIterator = new BiIterator<Section>(_dataModel.sections); | |
| 64 _feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds); | |
| 65 _articleIterator = | |
| 66 new BiIterator<Article>(_feedIterator.current.articles); | |
| 67 | |
| 68 currentArticle.addChangeListener((e) { | |
| 69 _articleIterator.jumpToValue(currentArticle.value); | |
| 70 }); | |
| 71 } | |
| 72 | |
| 73 /** | |
| 74 * Registers an event to fire on any state change | |
| 75 * | |
| 76 * TODO(jmesserly): fix this so we don't have to enumerate all of our fields | |
| 77 * again. One idea here is UIState becomes Observable, Observables have | |
| 78 * parents and notifications bubble up the parent chain. | |
| 79 */ | |
| 80 void addChangeListener(ChangeListener listener) { | |
| 81 _sectionIterator.currentIndex.addChangeListener(listener); | |
| 82 _feedIterator.currentIndex.addChangeListener(listener); | |
| 83 _articleIterator.currentIndex.addChangeListener(listener); | |
| 84 currentArticle.addChangeListener(listener); | |
| 85 } | |
| 86 | |
| 87 Map<String, String> toHistory() { | |
| 88 final data = {}; | |
| 89 data['section'] = currentSection.id; | |
| 90 data['feed'] = currentFeed.id; | |
| 91 if (currentArticle.value != null) { | |
| 92 data['article'] = currentArticle.value.id; | |
| 93 } | |
| 94 return data; | |
| 95 } | |
| 96 | |
| 97 void loadFromHistory(Map values) { | |
| 98 // TODO(jimhug): There's a better way of doing this... | |
| 99 if (values['section'] != null) { | |
| 100 _sectionIterator.jumpToValue(_dataModel. | |
| 101 findSectionById(values['section'])); | |
| 102 } else { | |
| 103 _sectionIterator = new BiIterator<Section>(_dataModel.sections); | |
| 104 } | |
| 105 if (values['feed'] != null && currentSection != null) { | |
| 106 _feedIterator.jumpToValue(currentSection.findFeed(values['feed'])); | |
| 107 } else { | |
| 108 _feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds); | |
| 109 } | |
| 110 if (values['article'] != null && currentFeed != null) { | |
| 111 currentArticle.value = currentFeed.findArticle(values['article']); | |
| 112 _articleIterator.jumpToValue(currentArticle.value); | |
| 113 } else { | |
| 114 _articleIterator = | |
| 115 new BiIterator<Article>(_feedIterator.current.articles); | |
| 116 currentArticle.value = null; | |
| 117 } | |
| 118 | |
| 119 storyMaximized.value = false; | |
| 120 } | |
| 121 | |
| 122 /** | |
| 123 * Move the currentArticle pointer to the next item in the Feed. | |
| 124 */ | |
| 125 void goToNextArticle() { | |
| 126 currentArticle.value = _articleIterator.next(); | |
| 127 selectedArticle.value = _articleIterator.current; | |
| 128 } | |
| 129 | |
| 130 /** | |
| 131 * Move the currentArticle pointer to the previous item in the Feed. | |
| 132 */ | |
| 133 void goToPreviousArticle() { | |
| 134 currentArticle.value = _articleIterator.previous(); | |
| 135 selectedArticle.value = _articleIterator.current; | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Move the selectedArticle pointer to the next item in the Feed. | |
| 140 */ | |
| 141 void goToNextSelectedArticle() { | |
| 142 selectedArticle.value = _articleIterator.next(); | |
| 143 } | |
| 144 | |
| 145 /** | |
| 146 * Move the selectedArticle pointer to the previous item in the Feed. | |
| 147 */ | |
| 148 void goToPreviousSelectedArticle() { | |
| 149 selectedArticle.value = _articleIterator.previous(); | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * Move the pointers for selectedArticle to point to the next | |
| 154 * Feed. | |
| 155 */ | |
| 156 void goToNextFeed() { | |
| 157 var newFeed = _feedIterator.next(); | |
| 158 int oldIndex = _articleIterator.currentIndex.value; | |
| 159 | |
| 160 _articleIterator = new BiIterator<Article>(newFeed.articles, | |
| 161 _articleIterator.currentIndex.listeners); | |
| 162 | |
| 163 _articleIterator.currentIndex.value = oldIndex; | |
| 164 selectedArticle.value = _articleIterator.current; | |
| 165 } | |
| 166 | |
| 167 /** | |
| 168 * Move the pointers for selectedArticle to point to the previous | |
| 169 * DataSource. | |
| 170 */ | |
| 171 void goToPreviousFeed() { | |
| 172 var newFeed = _feedIterator.previous(); | |
| 173 int oldIndex = _articleIterator.currentIndex.value; | |
| 174 | |
| 175 _articleIterator = new BiIterator<Article>(newFeed.articles, | |
| 176 _articleIterator.currentIndex.listeners); | |
| 177 _articleIterator.currentIndex.value = oldIndex; | |
| 178 selectedArticle.value = _articleIterator.current; | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Move to the next section (page) of feeds in the UI. | |
| 183 * @param index the previous index (how far down in a given feed) | |
| 184 * from the Source we are moving from. | |
| 185 * This method takes sliderMenu in the event that it needs to move | |
| 186 * to a previous section, it can notify the UI to update. | |
| 187 */ | |
| 188 void goToNextSection(SliderMenu sliderMenu) { | |
| 189 //TODO(efortuna): move sections? | |
| 190 var oldSection = currentSection; | |
| 191 int oldIndex = _articleIterator.currentIndex.value; | |
| 192 sliderMenu.selectNext(true); | |
| 193 // This check prevents our selector from wrapping around when we try to | |
| 194 // go to the "next section", but we're already at the last section. | |
| 195 if (oldSection != _sectionIterator.current) { | |
| 196 _feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds, | |
| 197 _feedIterator.currentIndex.listeners); | |
| 198 _articleIterator = | |
| 199 new BiIterator<Article>(_feedIterator.current.articles, | |
| 200 _articleIterator.currentIndex.listeners); | |
| 201 _articleIterator.currentIndex.value = oldIndex; | |
| 202 selectedArticle.value = _articleIterator.current; | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 /** | |
| 207 * Move to the previous section (page) of feeds in the UI. | |
| 208 * @param index the previous index (how far down in a given feed) | |
| 209 * from the Source we are moving from. | |
| 210 * @param oldSection the original starting section (before the slider | |
| 211 * menu moved) | |
| 212 * This method takes sliderMenu in the event that it needs to move | |
| 213 * to a previous section, it can notify the UI to update. | |
| 214 */ | |
| 215 void goToPreviousSection(SliderMenu sliderMenu) { | |
| 216 //TODO(efortuna): don't pass sliderMenu here. Just update in view! | |
| 217 var oldSection = currentSection; | |
| 218 int oldIndex = _articleIterator.currentIndex.value; | |
| 219 sliderMenu.selectPrevious(true); | |
| 220 | |
| 221 // This check prevents our selector from wrapping around when we try to | |
| 222 // go to the "previous section", but we're already at the first section. | |
| 223 if (oldSection != _sectionIterator.current) { | |
| 224 _feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds, | |
| 225 _feedIterator.currentIndex.listeners); | |
| 226 // Jump to back of feed set if we are moving backwards through sections. | |
| 227 _feedIterator.currentIndex.value = _feedIterator.list.length - 1; | |
| 228 _articleIterator = | |
| 229 new BiIterator<Article>(_feedIterator.current.articles, | |
| 230 _articleIterator.currentIndex.listeners); | |
| 231 _articleIterator.currentIndex.value = oldIndex; | |
| 232 selectedArticle.value = _articleIterator.current; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 /** | |
| 237 * Set the selected story as the current story (for viewing in the larger | |
| 238 * Story View.) | |
| 239 */ | |
| 240 void selectStoryAsCurrent() { | |
| 241 currentArticle.value = _articleIterator.current; | |
| 242 selectedArticle.value = _articleIterator.current; | |
| 243 } | |
| 244 | |
| 245 /** | |
| 246 * Remove our currentArticle selection, to move back to the Main Grid view. | |
| 247 */ | |
| 248 void clearCurrentArticle() { | |
| 249 currentArticle.value = null; | |
| 250 } | |
| 251 | |
| 252 /** | |
| 253 * Set the selectedArticle as the first item in that section (UI page). | |
| 254 */ | |
| 255 void goToFirstArticleInSection() { | |
| 256 selectedArticle.value = _articleIterator.current; | |
| 257 } | |
| 258 | |
| 259 /** | |
| 260 * Returns true if the UI is currently in the Story View state. | |
| 261 */ | |
| 262 bool get inMainView() => currentArticle.value == null; | |
| 263 | |
| 264 /** | |
| 265 * Returns true if we currently have an Article selected (for keyboard | |
| 266 * shortcuts browsing). | |
| 267 */ | |
| 268 bool get hasArticleSelected() => selectedArticle.value != null; | |
| 269 | |
| 270 /** | |
| 271 * Mark the current article as read | |
| 272 */ | |
| 273 bool markCurrentAsRead() { | |
| 274 currentArticle.value.unread.value = false; | |
| 275 } | |
| 276 | |
| 277 /** | |
| 278 * The user has moved to a new section (page). This can occur either | |
| 279 * if the user clicked on a section page, or used keyboard shortcuts. | |
| 280 * The default behavior is to move to the first article in the first | |
| 281 * column. The location of the selected item depends on the previous | |
| 282 * selected item location if the user used keyboard shortcuts. These | |
| 283 * are manipulated in goToPrevious/NextSection(). | |
| 284 */ | |
| 285 void moveToNewSection(String sectionTitle) { | |
| 286 _sectionIterator.currentIndex.value = | |
| 287 _dataModel.findSectionIndex(sectionTitle); | |
| 288 _feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds, | |
| 289 _feedIterator.currentIndex.listeners); | |
| 290 _articleIterator = | |
| 291 new BiIterator<Article>(_feedIterator.current.articles, | |
| 292 _articleIterator.currentIndex.listeners); | |
| 293 } | |
| 294 | |
| 295 Section get currentSection() => _sectionIterator.current; | |
| 296 Feed get currentFeed() => _feedIterator.current; | |
| 297 } | |
| OLD | NEW |