-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathdiscovery.ur
258 lines (253 loc) · 9.48 KB
/
discovery.ur
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
open Datatypes
open Utils
open SubItem
structure P = Popups
val discoveryTextBoxId = Unsafe.id "Discovery.discoveryTextBoxId"
val discoveryTopics =
"news"
:: "tech"
:: "comics"
:: "apple"
:: "photo"
:: "music"
:: "sports"
:: "games"
:: "science"
:: "politics"
:: "finance"
:: "fun"
:: "food"
:: "cooking"
:: "humor"
:: "design"
(* :: [] *)
(* val discoveryTopics2 = *)
:: "podcasts"
:: "programming"
:: "business"
:: "travel"
:: "software"
:: "mac"
:: "android"
:: "linux"
:: "books"
:: "entertainment"
:: "health"
:: "security"
:: "economics"
:: "art"
:: "fashion"
:: "fitness"
:: "beauty"
:: "productivity"
:: "history"
:: "philosophy"
:: "education"
:: []
con mtvmMap = { MtvmMap : {} }
ffi newMtvmMap : list (string * msgTreeViewMode) -> transaction mtvmMap
ffi updateMtvmMap : string -> msgTreeViewMode -> mtvmMap -> transaction {}
ffi clearMtvmMap : string -> mtvmMap -> transaction {}
ffi lookupMtvmMap : string -> mtvmMap -> transaction (option msgTreeViewMode)
fun discoveryWidget addSub opmlUploadClick displayDiscovery =
text <- source "";
country <- source "RU";
contents <- source <xml/>;
showImport <- source True;
cache <- source [];
contentsId <- fresh;
looksLikeUrl <- source False;
lastQuery <- source " ";(* чтобы первый запрос отработал и показал топики *)
nonEmptyText <- source False;
mtvms <- bind (newMtvmMap []) source;
let fun modMtvm m =
bind (get mtvms) m;
bind (get cache) (List.app (fn (_,(_,r)) =>
case r of
| Some (x,vms) => m vms
| None => return ()))
(* TODO: по хорошему, не надо обновлять режимы просмотра,
а использовать те, что уже есть в основном окне.
Тогда и setMtvm не понадобится. А то, при забитом кеше в discovery
на 2.5k подписок работает где-то 2.5сек (вместо 0.3 без setMtvm)
*)
fun setMtvm url mtvm =
set cache [];
(* ^ тупо убиваем кеш, чтобы не тормозило на куче подписок *)
modMtvm (updateMtvmMap url mtvm)
(* (fn x => (url,mtvm) :: List.filter (fn (u,_) => u <> url) x) *)
fun clearMtvm url =
modMtvm (clearMtvmMap url) (* (List.filter (fn (u,_) => u <> url)) *)
fun lookupMtvm url =
ms <- get mtvms;
mtvm <- lookupMtvmMap url ms(* List.assoc url ms *);
return (Option.get (modifyF [#UnreadOnly] (const False)
defaultMsgTreeViewMode) mtvm)
val getQuery =
v <- get text;
set nonEmptyText (v <> "");
return (Js.cleanDiscoveryQuery v)
fun lookupCache co query =
ca <- get cache;
time <- now;
return (case List.assoc (co, query) ca of
| Some (t, r) =>
if diffInSeconds t time >= 3600 then None else Some r
| _ => None)
fun insertCache co query (r : option (xbody * mtvmMap)) =
ca <- get cache;
time <- now;
set cache (((co, query), (time, r)) ::
List.take 14
(List.filter (fn (k,(t,_)) =>
k <> (co, query) &&
diffInSeconds t time < 3600) ca))
val hide =
stopPropagation; preventDefault;
Js.blur discoveryTextBoxId;
set displayDiscovery False;
Js.discoveryClearSelection
val selectCountry =
tid <- fresh;
d <- P.newBox
"Select country for subscriptions search"
<xml><ctextbox id={tid} class="selectSubscriptionInput" size={30}
dir={Js.dirAuto} /></xml>;
P.toggle d;
Js.setupCountryAutocomplete tid
(fn co =>
set country co;
P.hide;
BackgroundRpc.handleBGActions
(BGSetCountry { Country = co } :: []));
Js.select tid;
Js.focus tid
fun setContents (c,ms : transaction mtvmMap) =
set contents c;
Js.setScrollTop contentsId 0.0;
bind ms (set mtvms)
fun topicsList topics =
List.mapX (fn topic => <xml>
<div onclick={fn e =>
set text ("#" ^ topic);
when (not (Js.hasOnscreenKeyboard ()))
(* ^ на мобилах не выделяем *)
(Js.select discoveryTextBoxId);
search ()
}
class="discoveryTopic">{[topic]}</div></xml>)
topics
and default () = <xml>
<div class={Css.discoveryTopics}>Topics</div>
{topicsList discoveryTopics}
<div class={Css.discoveryCountry}>Country</div>
<div class={Css.discoveryCountryName}>
{dyn_ (co <- signal country;
return (txt (Js.countryNameFromCountryCode co)))}
</div>
<div class={Css.discoveryCountryButton}>
{textButton "Change" selectCountry}
</div>
</xml>
and cleanSearch () =
set text "";
search ()
and add () =
u <- getQuery;
hide;
cleanSearch ();
addSub u
and setSearchResult (r : option (xbody * mtvmMap)) =
set showImport False;
q <- getQuery;
case r of
| Some (x,ms) =>
setContents (x, return ms)
| None =>
setContents (<xml><div class="discoveryNotFound">
{if Js.discoveryQueryLooksLikeUrl q then
<xml>Click “Add” or press Enter key to find the feed on site.</xml>
else
<xml>Try enter the full feed or site URL.</xml>}
</div></xml>, newMtvmMap [])
and search () =
query <- getQuery;
set looksLikeUrl (Js.discoveryQueryLooksLikeUrl query);
lq <- get lastQuery;
when (query <> lq)
(set lastQuery query;
if query <> "" then
co <- get country;
cr <- lookupCache co query;
case cr of
| Some x =>
setSearchResult x
| _ =>
r <- tryRpc (Rpcs.discover co query);
(case r of
| Some x0 =>
x <- (case x0 of
None => return None
| Some (x,msl) =>
ms <- newMtvmMap msl;
return (Some (x,ms)));
insertCache co query x;
q' <- getQuery;
when (q' = query) (setSearchResult x)
| None =>
return ())
else
set showImport True;
setContents (default (), newMtvmMap []))
val tryAdd =
q <- getQuery;
if Js.discoveryQueryLooksLikeUrl q then
add ()
else
search ()
in
spawn (search ());
dummy <- fresh;
return { Hide = hide
, Country = country
, GetQuery = getQuery
, GetCountry = get country
, SetMtvm = setMtvm
, ClearMtvm = clearMtvm
, LookupMtvm = lookupMtvm
, Html = <xml>
<div class="discoveryHeader">
<div class="discoveryHeaderHint">Enter URL, title or #topic</div>
<div dynClass={ne <- signal nonEmptyText;
return (ifClass ne
Css.discoveryNonEmptyText
Css.oneLineInputAndButton)}>
{divClass Css.oneLineInputAndButtonBorder <xml>
<ctextbox id={discoveryTextBoxId}
class="feedUrlInput oneLineInputAndButtonInput" source={text} size={30}
dir={Js.dirAuto}
oninput={search ()}
onkeydown={onEnter (Js.setTimeout "tryAdd" tryAdd 0)}
/>
</xml>}
<div class="discoveryCleanButton"
onclick={fn _ => cleanSearch ();
when (not (Js.hasOnscreenKeyboard ()))
(Js.focus discoveryTextBoxId)}>×</div>
<div dynClass={ifClassS looksLikeUrl Css.discoveryAddDisabled
(return (classes Css.textButton Css.oneLineInputAndButtonButton))}
onclick={fn _ => tryAdd}>
{buttonText "Add"}
</div>
</div>
{displayIf showImport <xml>
<div class="discoveryImportButton">
{Js.opmlForm (textButton "Import OPML" opmlUploadClick)}
</div></xml>}
</div>
<div id={contentsId} class="discoveryContents flexFullHeight">
{dyn_ (signal contents)}
<div class="subscriptionsPadder"></div>
</div>
</xml> }
end