-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathTopicList.coffee
More file actions
404 lines (344 loc) · 16 KB
/
TopicList.coffee
File metadata and controls
404 lines (344 loc) · 16 KB
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
class TopicList extends Class
constructor: ->
@thread_sorter = null
@parent_topic_uri = undefined
@list_all = false
@topic_parent_uris = {}
@topic_sticky_uris = {}
actionList: (parent_topic_id, parent_topic_user_address) ->
for topic_sticky_uri in Page.site_info.content.settings.topic_sticky_uris
@topic_sticky_uris[topic_sticky_uri] = 1
$(".topics-loading").cssLater("top", "0px", 200)
# Topic group listing
if parent_topic_id
$(".topics-title").html(" ")
@parent_topic_uri = "#{parent_topic_id}_#{parent_topic_user_address}"
# Update visited info
Page.local_storage["topic.#{parent_topic_id}_#{parent_topic_user_address}.visited"] = Time.timestamp()
Page.cmd "wrapperSetLocalStorage", Page.local_storage
else
$(".topics-title").html("Newest topics")
@loadTopics("noanim")
# Show create new topic form
$(".topic-new-link").on "click", =>
if Page.site_info.address == "1TaLkFrMwvbNsooF4ioKAY9EuxTBTjipT"
$(".topmenu").css("background-color", "#fffede")
$(".topic-new .message").css("display", "block")
$(".topic-new").fancySlideDown()
$(".topic-new-link").slideUp()
return false
# Create new topic
$(".topic-new .button-submit").on "click", =>
@submitCreateTopic()
return false
$(".topics-more").on "click", =>
@list_all = true
$(".topics-more").text("Loading...")
@loadTopics("noanim")
return false
# Follow button
@initFollowButton()
initFollowButton: ->
@follow = new Follow($(".feed-follow-list"))
if @parent_topic_uri # Subtopic
@follow.addFeed("New topics in this group", "
SELECT
title AS title,
body,
added AS date_added,
'topic' AS type,
'?Topic:' || topic.topic_id || '_' || topic_creator_json.directory AS url,
parent_topic_uri AS param
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
WHERE parent_topic_uri IN (:params)
", true, @parent_topic_uri)
else
@follow.addFeed("New topics", "
SELECT
title AS title,
body,
added AS date_added,
'topic' AS type,
'?Topic:' || topic.topic_id || '_' || topic_creator_json.directory AS url
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
WHERE parent_topic_uri IS NULL
", true)
if Page.site_info.cert_user_id
username = Page.site_info.cert_user_id.replace /@.*/, ""
@follow.addFeed("Username mentions", "
SELECT
'mention' AS type,
comment.added AS date_added,
topic.title,
commenter_user.value || ': ' || comment.body AS body,
topic_creator_json.directory AS topic_creator_address,
topic.topic_id || '_' || topic_creator_json.directory AS row_topic_uri,
'?Topic:' || topic.topic_id || '_' || topic_creator_json.directory AS url
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
LEFT JOIN comment ON (comment.topic_uri = row_topic_uri)
LEFT JOIN json AS commenter_json ON (commenter_json.json_id = comment.json_id)
LEFT JOIN json AS commenter_content ON (commenter_content.directory = commenter_json.directory AND commenter_content.file_name = 'content.json')
LEFT JOIN keyvalue AS commenter_user ON (commenter_user.json_id = commenter_content.json_id AND commenter_user.key = 'cert_user_id')
WHERE
comment.body LIKE '%[#{username}%' OR comment.body LIKE '%@#{username}%'
", true)
@follow.addFeed("All comments", "
SELECT
'comment' AS type,
comment.added AS date_added,
topic.title,
commenter_user.value || ': ' || comment.body AS body,
topic_creator_json.directory AS topic_creator_address,
topic.topic_id || '_' || topic_creator_json.directory AS row_topic_uri,
'?Topic:' || topic.topic_id || '_' || topic_creator_json.directory AS url
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
LEFT JOIN comment ON (comment.topic_uri = row_topic_uri)
LEFT JOIN json AS commenter_json ON (commenter_json.json_id = comment.json_id)
LEFT JOIN json AS commenter_content ON (commenter_content.directory = commenter_json.directory AND commenter_content.file_name = 'content.json')
LEFT JOIN keyvalue AS commenter_user ON (commenter_user.json_id = commenter_content.json_id AND commenter_user.key = 'cert_user_id')
")
@follow.init()
loadTopics: (type="list", cb=false) ->
@logStart "Load topics..."
if @parent_topic_uri # Topic group listing
where = "WHERE parent_topic_uri = '#{@parent_topic_uri}' OR row_topic_uri = '#{@parent_topic_uri}'"
else # Main listing
where = "WHERE topic.type IS NULL AND topic.parent_topic_uri IS NULL"
last_elem = $(".topics-list .topic.template")
sql_sticky = ("WHEN '#{topic_uri}' THEN 1" for topic_uri in Page.site_info.content.settings.topic_sticky_uris).join(" ")
query = """
SELECT
COUNT(comment_id) AS comments_num, MAX(comment.added) AS last_comment, topic.added as last_added, CASE WHEN MAX(comment.added) IS NULL THEN topic.added ELSE MAX(comment.added) END as last_action,
topic.*,
topic_creator_user.value AS topic_creator_user_name,
topic_creator_content.directory AS topic_creator_address,
topic.topic_id || '_' || topic_creator_content.directory AS row_topic_uri,
NULL AS row_topic_sub_uri, NULL AS row_topic_sub_title,
(SELECT COUNT(*) FROM topic_vote WHERE topic_vote.topic_uri = topic.topic_id || '_' || topic_creator_content.directory) AS votes,
CASE topic.topic_id || '_' || topic_creator_content.directory #{sql_sticky} ELSE 0 END AS sticky
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
LEFT JOIN json AS topic_creator_content ON (topic_creator_content.directory = topic_creator_json.directory AND topic_creator_content.file_name = 'content.json')
LEFT JOIN keyvalue AS topic_creator_user ON (topic_creator_user.json_id = topic_creator_content.json_id AND topic_creator_user.key = 'cert_user_id')
LEFT JOIN comment ON (comment.topic_uri = row_topic_uri AND comment.added < #{Date.now()/1000+120})
#{where}
GROUP BY topic.topic_id, topic.json_id
HAVING last_action < #{Date.now()/1000+120}
"""
if not @parent_topic_uri # Union topic groups
query += """
UNION ALL
SELECT
COUNT(comment_id) AS comments_num, MAX(comment.added) AS last_comment,
MAX(topic_sub.added) AS last_added,
CASE WHEN MAX(topic_sub.added) > MAX(comment.added) OR MAX(comment.added) IS NULL THEN MAX(topic_sub.added) ELSE MAX(comment.added) END as last_action,
topic.*,
topic_creator_user.value AS topic_creator_user_name,
topic_creator_content.directory AS topic_creator_address,
topic.topic_id || '_' || topic_creator_content.directory AS row_topic_uri,
topic_sub.topic_id || '_' || topic_sub_creator_content.directory AS row_topic_sub_uri,
topic_sub.title AS topic_sub_title,
(SELECT COUNT(*) FROM topic_vote WHERE topic_vote.topic_uri = topic.topic_id || '_' || topic_creator_content.directory) AS votes,
CASE topic.topic_id || '_' || topic_creator_content.directory #{sql_sticky} ELSE 0 END AS sticky
FROM topic
LEFT JOIN json AS topic_creator_json ON (topic_creator_json.json_id = topic.json_id)
LEFT JOIN json AS topic_creator_content ON (topic_creator_content.directory = topic_creator_json.directory AND topic_creator_content.file_name = 'content.json')
LEFT JOIN keyvalue AS topic_creator_user ON (topic_creator_user.json_id = topic_creator_content.json_id AND topic_creator_user.key = 'cert_user_id')
LEFT JOIN topic AS topic_sub ON (topic_sub.parent_topic_uri = topic.topic_id || '_' || topic_creator_content.directory)
LEFT JOIN json AS topic_sub_creator_json ON (topic_sub_creator_json.json_id = topic_sub.json_id)
LEFT JOIN json AS topic_sub_creator_content ON (topic_sub_creator_content.directory = topic_sub_creator_json.directory AND topic_sub_creator_content.file_name = 'content.json')
LEFT JOIN comment ON (comment.topic_uri = row_topic_sub_uri AND comment.added < #{Date.now()/1000+120})
WHERE topic.type = "group"
GROUP BY topic.topic_id
HAVING last_action < #{Date.now()/1000+120}
"""
if not @list_all and not @parent_topic_uri
query += " ORDER BY sticky DESC, last_action DESC LIMIT 30"
Page.cmd "dbQuery", [query], (topics) =>
topics.sort (a,b) ->
booster_a = booster_b = 0
# Boost position to top for sticky topics
if window.TopicList.topic_sticky_uris[a.row_topic_uri]
booster_a = window.TopicList.topic_sticky_uris[a.row_topic_uri]*10000000
if window.TopicList.topic_sticky_uris[b.row_topic_uri]
booster_b = window.TopicList.topic_sticky_uris[b.row_topic_uri]*10000000
return Math.max(b.last_comment+booster_b, b.last_added+booster_b)-Math.max(a.last_comment+booster_a, a.last_added+booster_a)
for topic in topics
topic_uri = topic.row_topic_uri
if topic.last_added
topic.added = topic.last_added
# Parent topic for group that we currently listing
if @parent_topic_uri and topic_uri == @parent_topic_uri
topic_parent = topic
continue # Dont display it
elem = $("#topic_"+topic_uri)
if elem.length == 0 # Create if not exits yet
elem = $(".topics-list .topic.template").clone().removeClass("template").attr("id", "topic_"+topic_uri)
if type != "noanim" then elem.cssSlideDown()
@applyTopicListeners(elem, topic)
elem.insertAfter(last_elem)
last_elem = elem
@applyTopicData(elem, topic)
Page.addInlineEditors()
$("body").css({"overflow": "auto", "height": "auto"}) # Auto height body
@logEnd "Load topics..."
# Hide loading
if parseInt($(".topics-loading").css("top")) > -30 # Loading visible, animate it
$(".topics-loading").css("top", "-30px")
else
$(".topics-loading").remove()
# Set sub-title listing title
if @parent_topic_uri
$(".topics-title").html("<span class='parent-link'><a href='?Main'>" + "Main" + "</a> ›</span> #{topic_parent.title}")
$(".topics").css("opacity", 1)
# Show loading / empty forum bigmessage
if topics.length == 0
if Page.site_info.bad_files
$(".message-big").text("Initial sync in progress...")
else
$(".message-big").text("Welcome to your own forum! :)")
$(".topic-new-link").trigger("click")
$(".message-big").css("display", "block").cssLater("opacity", 1)
else
$(".message-big").css("display", "none")
if topics.length == 30
$(".topics-more").css("display", "block")
else
$(".topics-more").css("display", "none")
if cb then cb()
applyTopicListeners: (elem, topic) ->
# User hide
$(".user_menu", elem).on "click", =>
menu = new Menu($(".user_menu", elem))
menu.addItem "Mute this user", =>
elem.fancySlideUp()
Page.cmd "muteAdd", [topic.topic_creator_address, topic.topic_creator_user_name, "Topic: #{topic.title}"]
menu.show()
return false
applyTopicData: (elem, topic, type="list") ->
title_hash = Text.toUrl(topic.title)
topic_uri = topic.row_topic_uri
$(".title .title-link", elem).text(topic.title)
$(".title .title-link, a.image, .comment-num", elem).attr("href", "?Topic:#{topic_uri}/#{title_hash}")
elem.data "topic_uri", topic_uri
# Get links in body
body = topic.body
url_match = body.match /http[s]{0,1}:\/\/[^"', \r\n)$]+/
if topic.type == "group" # Group type topic
$(elem).addClass("topic-group")
$(".image .icon", elem).removeClass("icon-topic-chat").addClass("icon-topic-group")
$(".link", elem).css("display", "none")
#$(".info", elem).css("display", "none")
$(".title .title-link, a.image, .comment-num", elem).attr("href", "?Topics:#{topic_uri}/#{title_hash}")
else if url_match # Link type topic
url = url_match[0]
if type != "show" then body = body.replace /http[s]{0,1}:\/\/[^"' \r\n)$]+$/g, "" # Remove links from end
$(".image .icon", elem).removeClass("icon-topic-chat").addClass("icon-topic-link")
$(".link", elem).css("display", "").attr "href", Text.fixLink(url)
$(".link .link-url", elem).text(url)
else # Normal type topic
$(".image .icon", elem).removeClass("icon-topic-link").addClass("icon-topic-chat")
$(".link", elem).css("display", "none")
if type == "show" # Markdon syntax at topic show
$(".body", elem).html Text.toMarked(body, {"sanitize": true})
else # No format on listing
$(".body", elem).text body
if window.TopicList.topic_sticky_uris[topic_uri]
elem.addClass("topic-sticky")
# Last activity and comment num
if type != "show"
last_action = Math.max(topic.last_comment, topic.added)
if topic.type == "group"
$(".comment-num", elem).text "last activity"
$(".added", elem).text Time.since(last_action)
else if topic.comments_num > 0
$(".comment-num", elem).text "#{topic.comments_num} comment"
$(".added", elem).text "last "+Time.since(last_action)
else
$(".comment-num", elem).text "0 comments"
$(".added", elem).text Time.since(last_action)
# Creator address and user name
$(".user_name", elem)
.text(topic.topic_creator_user_name.replace(/@.*/, ""))
.attr("title", topic.topic_creator_user_name+": "+topic.topic_creator_address)
# Apply topic score
if User.my_topic_votes[topic_uri] # Voted on topic
$(".score-inactive .score-num", elem).text topic.votes-1
$(".score-active .score-num", elem).text topic.votes
$(".score", elem).addClass("active")
else # Not voted on it
$(".score-inactive .score-num", elem).text topic.votes
$(".score-active .score-num", elem).text topic.votes+1
$(".score", elem).off("click").on "click", @submitTopicVote
# Visited
visited = Page.local_storage["topic.#{topic_uri}.visited"]
if not visited
elem.addClass("visit-none")
else if visited < last_action
elem.addClass("visit-newcomment")
if type == "show" then $(".added", elem).text Time.since(topic.added)
# Sub-topic
if topic.row_topic_sub_title
subtopic_title_hash = Text.toUrl(topic.row_topic_sub_title)
subtopic_uri = topic.row_topic_sub_uri
$(".subtopic", elem)
.css("display", "block")
$(".subtopic-link", elem)
.attr("href", "?Topic:#{subtopic_uri}/#{subtopic_title_hash}")
.text(topic.row_topic_sub_title)
# My topic
if topic.topic_creator_address == Page.site_info.auth_address
$(elem).attr("data-object", "Topic:#{topic_uri}").attr("data-deletable", "yes")
$(".title .title-link", elem).attr("data-editable", "title").data("content", topic.title)
$(".body", elem).attr("data-editable", "body").data("content", topic.body)
submitCreateTopic: ->
# if not Page.hasOpenPort() then return false
if not Page.site_info.cert_user_id # No selected cert
Page.cmd "certSelect", [["zeroid.bit"]]
return false
title = $(".topic-new #topic_title").val().trim()
body = $(".topic-new #topic_body").val().trim()
#if not body then return $(".topic-new #topic_body").focus()
if not title then return $(".topic-new #topic_title").focus()
$(".topic-new .button-submit").addClass("loading")
User.getData (data) =>
topic = {
"topic_id": data.next_topic_id,
"title": title,
"body": body,
"added": Time.timestamp()
}
if @parent_topic_uri then topic.parent_topic_uri = @parent_topic_uri
data.topic.push topic
data.next_topic_id += 1
User.publishData data, (res) =>
$(".topic-new .button-submit").removeClass("loading")
$(".topic-new").slideUp()
$(".topic-new-link").slideDown()
setTimeout (=>
@loadTopics()
), 600
$(".topic-new #topic_body").val("")
$(".topic-new #topic_title").val("")
submitTopicVote: (e) =>
if not Page.site_info.cert_user_id # No selected cert
Page.cmd "certSelect", [["zeroid.bit"]]
return false
elem = $(e.currentTarget)
elem.toggleClass("active").addClass("loading")
inner_path = "data/users/#{User.my_address}/data.json"
User.getData (data) =>
data.topic_vote ?= {}
topic_uri = elem.parents(".topic").data("topic_uri")
if elem.hasClass("active")
data.topic_vote[topic_uri] = 1
else
delete data.topic_vote[topic_uri]
User.publishData data, (res) =>
elem.removeClass("loading")
return false
window.TopicList = new TopicList()