forked from dart-lang/sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DataSource.dart
244 lines (202 loc) · 7.14 KB
/
DataSource.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// @dart = 2.9
part of swarmlib;
/** The top-level collection of all sections for a user. */
// TODO(jimhug): This is known as UserData in the server model.
class Sections extends IterableBase<Section> {
final List<Section> _sections;
Sections(this._sections);
operator [](int i) => _sections[i];
int get length => _sections.length;
List<String> get sectionTitles => _sections.map((s) => s.title).toList();
void refresh() {
// TODO(jimhug): http://b/issue?id=5351067
}
/**
* Find the Section object that has a given title.
* This is used to integrate well with [ConveyorView].
*/
Section findSection(String name) {
return CollectionUtils.find(_sections, (sect) => sect.title == name);
}
// TODO(jimhug): Track down callers!
Iterator<Section> get iterator => _sections.iterator;
// TODO(jimhug): Better support for switching between local dev and server.
static bool get runningFromFile {
return window.location.protocol.startsWith('file:');
}
static String get home {
// TODO(jmesserly): window.location.origin not available on Safari 4.
// Move this workaround to the DOM code. See bug 5389503.
return '${window.location.protocol}//${window.location.host}';
}
// This method is exposed for tests.
static void initializeFromData(String data, void callback(Sections sects)) {
final decoder = new Decoder(data);
int nSections = decoder.readInt();
final sections = new List<Section>();
for (int i = 0; i < nSections; i++) {
sections.add(Section.decode(decoder));
}
callback(new Sections(sections));
}
static void initializeFromUrl(
bool useCannedData, void callback(Sections sections)) {
if (Sections.runningFromFile || useCannedData) {
initializeFromData(CannedData.data['user.data'], callback);
} else {
// TODO(jmesserly): display an error if we fail here! Silent failure bad.
HttpRequest
.getString('data/user.data')
.then(EventBatch.wrap((responseText) {
// TODO(jimhug): Nice response if get error back from server.
// TODO(jimhug): Might be more efficient to parse request
// in sections.
initializeFromData(responseText, callback);
}));
}
}
Section findSectionById(String id) {
return CollectionUtils.find(_sections, (section) => section.id == id);
}
/**
* Given the name of a section, find its index in the set.
*/
int findSectionIndex(String name) {
for (int i = 0; i < _sections.length; i++) {
if (name == _sections[i].title) {
return i;
}
}
return -1;
}
List<Section> get sections => _sections;
// TODO(jmesserly): this should be a property
bool get isEmpty => length == 0;
}
/** A collection of data sources representing a page in the UI. */
class Section {
final String id;
final String title;
ObservableList<Feed> feeds;
// Public for testing. TODO(jacobr): find a cleaner solution.
Section(this.id, this.title, this.feeds);
void refresh() {
for (final feed in feeds) {
// TODO(jimhug): http://b/issue?id=5351067
}
}
static Section decode(Decoder decoder) {
final sectionId = decoder.readString();
final sectionTitle = decoder.readString();
final nSources = decoder.readInt();
final feeds = new ObservableList<Feed>();
for (int j = 0; j < nSources; j++) {
feeds.add(Feed.decode(decoder));
}
return new Section(sectionId, sectionTitle, feeds);
}
Feed findFeed(String id_) {
return CollectionUtils.find(feeds, (feed) => feed.id == id_);
}
}
/** Provider of a news feed. */
class Feed {
String id;
final String title;
final String iconUrl;
final String description;
ObservableList<Article> articles;
ObservableValue<bool> error; // TODO(jimhug): Check if dead code.
Feed(this.id, this.title, this.iconUrl, {this.description: ''})
: articles = new ObservableList<Article>(),
error = new ObservableValue<bool>(false);
static Feed decode(Decoder decoder) {
final sourceId = decoder.readString();
final sourceTitle = decoder.readString();
final sourceIcon = decoder.readString();
final feed = new Feed(sourceId, sourceTitle, sourceIcon);
final nItems = decoder.readInt();
for (int i = 0; i < nItems; i++) {
feed.articles.add(Article.decodeHeader(feed, decoder));
}
return feed;
}
Article findArticle(String id_) {
return CollectionUtils.find(articles, (article) => article.id == id_);
}
void refresh() {}
}
/** A single article or posting to display. */
class Article {
final String id;
DateTime date;
final String title;
final String author;
final bool hasThumbnail;
String textBody; // TODO(jimhug): rename to snippet.
final Feed dataSource; // TODO(jimhug): rename to feed.
String _htmlBody;
String srcUrl;
final ObservableValue<bool> unread; // TODO(jimhug): persist to server.
bool error; // TODO(jimhug): Check if this is dead and remove.
Article(this.dataSource, this.id, this.date, this.title, this.author,
this.srcUrl, this.hasThumbnail, this.textBody,
{htmlBody: null, bool unread: true, this.error: false})
: unread = new ObservableValue<bool>(unread),
this._htmlBody = htmlBody;
String get htmlBody {
_ensureLoaded();
return _htmlBody;
}
String get dataUri {
return SwarmUri
.encodeComponent(id)
.replaceAll('%2F', '/')
.replaceAll('%253A', '%3A');
}
String get thumbUrl {
if (!hasThumbnail) return null;
var home;
if (Sections.runningFromFile) {
home = 'http://dart.googleplex.com';
} else {
home = Sections.home;
}
// By default images from the real server are cached.
// Bump the version flag if you change the thumbnail size, and you want to
// get the new images. Our server ignores the query params but it gets
// around appengine server side caching and the client side cache.
return 'data/$dataUri.jpg';
}
// TODO(jimhug): need to return a lazy Observable<String> and also
// add support for preloading.
void _ensureLoaded() {
if (_htmlBody != null) return;
var name = '$dataUri.html';
if (Sections.runningFromFile) {
_htmlBody = CannedData.data[name];
} else {
// TODO(jimhug): Remove this truly evil synchronoush xhr.
final req = new HttpRequest();
req.open('GET', 'data/$name', async: false);
req.send();
_htmlBody = req.responseText;
}
}
static Article decodeHeader(Feed source, Decoder decoder) {
final id = decoder.readString();
final title = decoder.readString();
final srcUrl = decoder.readString();
final hasThumbnail = decoder.readBool();
final author = decoder.readString();
final dateInSeconds = decoder.readInt();
final snippet = decoder.readString();
final date = new DateTime.fromMillisecondsSinceEpoch(dateInSeconds * 1000,
isUtc: true);
return new Article(
source, id, date, title, author, srcUrl, hasThumbnail, snippet);
}
}