liquid_feedback_frontend
diff app/main/issue/_list.lua @ 1309:32cc544d5a5b
Cumulative patch for upcoming frontend version 4
| author | bsw/jbe |
|---|---|
| date | Sun Jul 15 14:07:29 2018 +0200 (2018-07-15) |
| parents | |
| children | 3ab878ba277e |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/app/main/issue/_list.lua Sun Jul 15 14:07:29 2018 +0200 1.3 @@ -0,0 +1,425 @@ 1.4 +local for_member = param.get ( "for_member", "table" ) 1.5 +local for_unit = param.get ( "for_unit", "table" ) 1.6 +local for_area = param.get ( "for_area", "table" ) 1.7 +local for_issue = param.get ( "for_issue", "table" ) 1.8 +local for_initiative = param.get ( "for_initiative", "table" ) 1.9 +local for_sidebar = param.get("for_sidebar", atom.boolean) 1.10 +local no_filter = param.get ( "no_filter", atom.boolean ) 1.11 +local search = param.get ( "search" ) 1.12 + 1.13 +local limit = 25 1.14 + 1.15 +local mode = request.get_param{ name = "mode" } or "issue" 1.16 + 1.17 +if for_initiative or for_issue or for_member then 1.18 + mode = "timeline" 1.19 +end 1.20 + 1.21 +local selector 1.22 + 1.23 +if search then 1.24 + 1.25 + selector = Issue:get_search_selector(search) 1.26 + 1.27 + 1.28 +elseif mode == "timeline" then 1.29 + 1.30 + local event_max_id = request.get_param_strings()["event_max_id"] 1.31 + 1.32 + selector = Event:new_selector() 1.33 + :add_order_by("event.id DESC") 1.34 + :join("issue", nil, "issue.id = event.issue_id") 1.35 + :add_field("now() - event.occurrence", "time_ago") 1.36 + :limit(limit + 1) 1.37 + 1.38 + if event_max_id then 1.39 + selector:add_where{ "event.id < ?", event_max_id } 1.40 + end 1.41 + 1.42 + if for_member then 1.43 + selector:add_where{ "event.member_id = ?", for_member.id } 1.44 + end 1.45 + 1.46 + if for_initiative then 1.47 + selector:add_where{ "event.initiative_id = ?", for_initiative.id } 1.48 + end 1.49 + 1.50 + 1.51 +elseif mode == "issue" then 1.52 + 1.53 + selector = Issue:new_selector() 1.54 + 1.55 +end 1.56 + 1.57 +if for_unit then 1.58 + selector:join("area", nil, "area.id = issue.area_id") 1.59 + selector:add_where{ "area.unit_id = ?", for_unit.id } 1.60 +elseif for_area then 1.61 + selector:add_where{ "issue.area_id = ?", for_area.id } 1.62 +elseif for_issue then 1.63 + selector:add_where{ "issue.id = ?", for_issue.id } 1.64 +end 1.65 + 1.66 +if not search and app.session.member_id then 1.67 + selector 1.68 + :left_join("interest", "_interest", { 1.69 + "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id 1.70 + } ) 1.71 + :add_field("(_interest.member_id NOTNULL)", "is_interested") 1.72 + :left_join("delegating_interest_snapshot", "_delegating_interest", { [[ 1.73 + _delegating_interest.issue_id = issue.id AND 1.74 + _delegating_interest.member_id = ? AND 1.75 + _delegating_interest.snapshot_id = issue.latest_snapshot_id 1.76 + ]], app.session.member.id } ) 1.77 + :add_field("_delegating_interest.delegate_member_ids[1]", "is_interested_by_delegation_to_member_id") 1.78 + :add_field("_delegating_interest.delegate_member_ids[array_upper(_delegating_interest.delegate_member_ids, 1)]", "is_interested_via_member_id") 1.79 + :add_field("array_length(_delegating_interest.delegate_member_ids, 1)", "delegation_chain_length") 1.80 +end 1.81 + 1.82 +local function doit() 1.83 + 1.84 + local last_event_id 1.85 + 1.86 + local items = selector:exec() 1.87 + 1.88 + local row_class = "sectionRow" 1.89 + if for_sidebar then 1.90 + row_class = "sidebarRow" 1.91 + end 1.92 + 1.93 + if mode == "timeline" then 1.94 + local issues = items:load ( "issue" ) 1.95 + local initiative = items:load ( "initiative" ) 1.96 + items:load ( "suggestion" ) 1.97 + items:load ( "member" ) 1.98 + issues:load_everything_for_member_id ( app.session.member_id ) 1.99 + initiative:load_everything_for_member_id ( app.session.member_id ) 1.100 + elseif mode == "issue" then 1.101 + items:load_everything_for_member_id ( app.session.member_id ) 1.102 + end 1.103 + 1.104 + local last_event_date 1.105 + for i, item in ipairs(items) do 1.106 + local event 1.107 + local issue 1.108 + if mode == "timeline" then 1.109 + event = item 1.110 + issue = item.issue 1.111 + elseif mode == "issue" then 1.112 + event = {} 1.113 + issue = item 1.114 + end 1.115 + 1.116 + last_event_id = event.id 1.117 + 1.118 + local class = "mdl-card mdl-shadow--2dp mdl-card__fullwidth event " .. row_class 1.119 + if event.suggestion_id then 1.120 + class = class .. " suggestion" 1.121 + end 1.122 + 1.123 + ui.container{ attr = { class = class, }, content = function() 1.124 + local event_name 1.125 + local event_icon 1.126 + local negative_event = false 1.127 + 1.128 + local days_ago_text 1.129 + 1.130 + if mode == "timeline" then 1.131 + event_name = event.event_name 1.132 + if event.event == "issue_state_changed" then 1.133 + if event.state == "discussion" then 1.134 + event_name = _"Discussion started" 1.135 + elseif event.state == "verification" then 1.136 + event_name = _"Verification started" 1.137 + elseif event.state == "voting" then 1.138 + event_name = _"Voting started" 1.139 + elseif event.state == "finished_with_winner" then 1.140 + event_name = event.state_name 1.141 + elseif event.state == "finished_without_winner" then 1.142 + event_name = event.state_name 1.143 + negative_event = true 1.144 + else 1.145 + event_name = event.state_name 1.146 + negative_event = true 1.147 + end 1.148 + elseif event.event == "initiative_revoked" then 1.149 + negative_event = true 1.150 + end 1.151 + 1.152 + if event.time_ago == 0 then 1.153 + days_ago_text = _("today at #{time}", { time = format.time(event.occurrence) }) 1.154 + elseif event.time_ago == 1 then 1.155 + days_ago_text = _("yesterday at #{time}", { time = format.time(event.occurrence) }) 1.156 + else 1.157 + days_ago_text = _("#{interval} ago", { interval = format.interval_text ( event.time_ago ) } ) 1.158 + end 1.159 + 1.160 + elseif mode == "issue" then 1.161 + local event_icons_map = { 1.162 + admission = "bubble_chart", 1.163 + discussion = "question_answer", 1.164 + verification = "find_in_page", 1.165 + voting = "mail", 1.166 + finished_with_winner = "gavel", 1.167 + finished_without_winner = "gavel", 1.168 + canceled = "clear" 1.169 + } 1.170 + event_icon = event_icons_map[issue.state] or event_icons_map["canceled"] 1.171 + event_name = issue.state_name 1.172 + if issue.state_time_left:sub(1,1) ~= "-" then 1.173 + days_ago_text = _( "#{interval_text} left", { 1.174 + interval_text = format.interval_text ( issue.state_time_left ) 1.175 + }) 1.176 + elseif issue.closed then 1.177 + days_ago_text = _( "#{interval_text} ago", { 1.178 + interval_text = format.interval_text ( issue.closed_ago ) 1.179 + }) 1.180 + else 1.181 + days_ago_text = _"phase ends soon" 1.182 + end 1.183 + if issue.closed and not issue.fully_frozen then 1.184 + negative_event = true 1.185 + end 1.186 + if issue.state == "finished_without_winner" 1.187 + or issue.state == "canceled_no_initiative_admitted" 1.188 + or issue.state == "canceled_by_admin" 1.189 + then 1.190 + negative_event = true 1.191 + end 1.192 + end 1.193 + 1.194 + local class= "event_info" 1.195 + 1.196 + if negative_event then 1.197 + class = class .. " negative" 1.198 + end 1.199 + 1.200 + if not for_issue and not for_initiative then 1.201 + ui.container{ attr = { class = "mdl-card__title mdl-card--has-fab mdl-card--border" }, content = function() 1.202 + if not (config.single_unit_id and config.single_area_id) then 1.203 + if not config.single_unit_id then 1.204 + slot.put ( " " ) 1.205 + ui.link{ 1.206 + module = "index", view = "index", params = { unit = issue.area.unit_id }, 1.207 + attr = { class = "mdl-chip unit" }, content = function() 1.208 + ui.tag{ attr = { class = "mdl-chip__text" }, content = function() 1.209 + ui.tag{ tag = "i", attr = { class = "material-icons" }, content = "flag" } 1.210 + slot.put(" ") 1.211 + ui.tag{ content = issue.area.unit.name } 1.212 + end } 1.213 + end 1.214 + } 1.215 + end 1.216 + if not config.single_area_id then 1.217 + slot.put(" ") 1.218 + ui.link{ 1.219 + module = "index", view = "index", params = { unit = issue.area.unit_id, area = issue.area_id }, 1.220 + attr = { class = "mdl-chip area" }, content = function() 1.221 + ui.tag{ attr = { class = "mdl-chip__text" }, content = function() 1.222 + ui.tag{ tag = "i", attr = { class = "material-icons" }, content = "folder" } 1.223 + slot.put(" ") 1.224 + ui.tag{ content = issue.area.name } 1.225 + end} 1.226 + end 1.227 + } 1.228 + end 1.229 + end 1.230 + --ui.heading{ level = 2, attr = { class = "mdl-card__title-text", style = "display: block;" }, content = function() 1.231 + slot.put(" ") 1.232 + ui.link{ 1.233 + module = "issue", view = "show", id = issue.id, 1.234 + attr = { class = "mdl-chip issue" }, content = function() 1.235 + ui.tag{ tag = "i", attr = { class = "material-icons" }, content = "label" } 1.236 + slot.put(" ") 1.237 + ui.tag{ attr = { class = "mdl-chip__text" }, content = issue.name } 1.238 + end } 1.239 + --end } 1.240 + ui.container{ attr = { class = "mdl-card__subtitle-text .mdl-cell--hide-phone" }, content = function() 1.241 + ui.container{ attr = { class = class }, content = function () 1.242 + if event_icon then 1.243 + ui.tag{ tag = "i", attr = { class = "material-icons" }, content = event_icon } 1.244 + end 1.245 + slot.put(" ") 1.246 + ui.tag { content = event_name } 1.247 + slot.put(" ") 1.248 + ui.tag{ content = "(" .. days_ago_text .. ")" } 1.249 + end } 1.250 + end } 1.251 + if app.session.member and issue.fully_frozen and not issue.closed and not issue.member_info.direct_voted and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id) then 1.252 + ui.link { 1.253 + attr = { class = "mdl-button mdl-js-button mdl-button--fab mdl-button--colored" , 1.254 + style = "position: absolute; right: 20px; bottom: -27px;" 1.255 + }, 1.256 + module = "vote", view = "list", 1.257 + params = { issue_id = issue.id }, 1.258 + content = function() 1.259 + ui.tag{ tag = "i", attr = { class = "material-icons" }, content = config.voting_icon or "mail_outline" } 1.260 + end 1.261 + } 1.262 + end 1.263 + end } 1.264 + end 1.265 + 1.266 + if event.suggestion_id then 1.267 + ui.container{ attr = { class = "suggestion" }, content = function() 1.268 + ui.link{ 1.269 + text = format.string(event.suggestion.name, { 1.270 + truncate_at = 160, truncate_suffix = true 1.271 + }), 1.272 + module = "initiative", view = "show", id = event.initiative.id, 1.273 + params = { suggestion_id = event.suggestion_id }, 1.274 + anchor = "s" .. event.suggestion_id 1.275 + } 1.276 + end } 1.277 + end 1.278 + 1.279 + if not for_initiative and (not for_issue or event.initiative_id) then 1.280 + 1.281 + ui.container{ attr = { class = "initiative_list" }, content = function() 1.282 + if event.initiative_id then 1.283 + local initiative = event.initiative 1.284 + 1.285 + execute.view{ module = "initiative", view = "_list", params = { 1.286 + issue = issue, 1.287 + initiative = initiative, 1.288 + for_event = mode == "timeline" and not (event.state == issue.state) 1.289 + 1.290 + } } 1.291 + else 1.292 + local initiatives = issue.initiatives 1.293 + execute.view{ module = "initiative", view = "_list", params = { 1.294 + issue = issue, 1.295 + initiatives = initiatives, 1.296 + for_event = mode == "timeline" and not (event.state == issue.state) 1.297 + } } 1.298 + end 1.299 + end } 1.300 + end 1.301 + if app.session.member_id then 1.302 + ui.container{ attr = { class = "mdl-card__actions mdl-card--border" }, content = function() 1.303 + execute.view{ 1.304 + module = "delegation", view = "_info", params = { 1.305 + issue = issue, member = for_member 1.306 + } 1.307 + } 1.308 + end } 1.309 + end 1.310 + end } 1.311 + 1.312 + end 1.313 + 1.314 + if mode == "timeline" then 1.315 + if for_sidebar then 1.316 + ui.container { attr = { class = row_class }, content = function () 1.317 + ui.link{ 1.318 + attr = { class = "moreLink" }, 1.319 + text = _"Show full history", 1.320 + module = "initiative", view = "history", id = for_initiative.id 1.321 + } 1.322 + end } 1.323 + elseif #items > limit then 1.324 + ui.container { attr = { class = row_class }, content = function () 1.325 + local params = request.get_param_strings() 1.326 + ui.link{ 1.327 + attr = { class = "moreLink" }, 1.328 + text = _"Show older events", 1.329 + module = request.get_module(), 1.330 + view = request.get_view(), 1.331 + id = for_unit and for_unit.id or for_area and for_area.id or for_issue and for_issue.id or for_member and for_member.id, 1.332 + params = { 1.333 + mode = "timeline", 1.334 + event_max_id = last_event_id, 1.335 + tab = params["tab"], 1.336 + phase = params["phase"], 1.337 + closed = params["closed"] 1.338 + } 1.339 + } 1.340 + end } 1.341 + elseif #items < 1 then 1.342 + ui.container { attr = { class = row_class }, content = _"No more events available" } 1.343 + end 1.344 + end 1.345 + 1.346 + if #items < 1 then 1.347 + ui.section( function() 1.348 + ui.sectionRow( function() 1.349 + ui.container{ content = _"No results for this selection" } 1.350 + end ) 1.351 + end ) 1.352 + end 1.353 + 1.354 + 1.355 +end 1.356 + 1.357 + 1.358 +local filters = {} 1.359 + 1.360 +if not for_initiative and not for_issue and not no_filter then 1.361 + 1.362 + filters = execute.chunk{ 1.363 + module = "issue", chunk = "_filters", params = { 1.364 + for_events = mode == "timeline" and true or false, 1.365 + member = app.session.member, 1.366 + for_member = for_member, 1.367 + state = for_state, 1.368 + for_unit = for_unit and true or false, 1.369 + for_area = for_area and true or false 1.370 + }} 1.371 +end 1.372 + 1.373 +filters.opened = true 1.374 +filters.selector = selector 1.375 + 1.376 + 1.377 +local function dotabs() 1.378 + slot.select("filter", function() 1.379 + ui.container{ attr = { class = "mdl-tabs mdl-js-tabs mdl-js-ripple-effect float-left" }, content = function() 1.380 + ui.container{ attr = { class = "mdl-tabs__tab-bar" }, content = function() 1.381 + local mode = request.get_param{ name = "mode" } 1.382 + local css_active = (not mode or mode == "issue") and " is-active" or "" 1.383 + ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), content = "Issues", attr = { class = "mdl-tabs__tab" .. css_active } } 1.384 + local css_active = mode and " is-active" or " " 1.385 + ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), params = { mode = "timeline" }, content = "Timeline", attr = { class = "mdl-tabs__tab" .. css_active } } 1.386 + ui.link{ module = "member", view = "list", content = "Member", attr = { class = "mdl-tabs__tab" } } 1.387 + end } 1.388 + end } 1.389 + end) 1.390 +end 1.391 + 1.392 + 1.393 +if mode == "timeline" then 1.394 + --dotabs() 1.395 + filters.content = function() 1.396 + execute.view{ module = "index", view = "_head" } 1.397 + doit() 1.398 + end 1.399 +else 1.400 + -- dotabs() 1.401 + filters.content = function() 1.402 + if config.voting_only then 1.403 + local admission_order_field = "filter_issue_order.order_in_unit" 1.404 + if for_area then 1.405 + admission_order_field = "filter_issue_order.order_in_area" 1.406 + end 1.407 + selector:left_join ( "issue_order_in_admission_state", "filter_issue_order", "filter_issue_order.id = issue.id" ) 1.408 + selector:add_order_by ( "issue.closed DESC NULLS FIRST" ) 1.409 + selector:add_order_by ( "issue.accepted ISNULL" ) 1.410 + selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN NULL ELSE justify_interval(coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.max_admission_time) - now()) END" ) 1.411 + selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN " .. admission_order_field .. " ELSE NULL END" ) 1.412 + selector:add_order_by ( "id" ) 1.413 + end 1.414 + execute.view{ module = "index", view = "_head" } 1.415 + ui.paginate{ 1.416 + selector = selector, 1.417 + per_page = 25, 1.418 + content = doit 1.419 + } 1.420 + end 1.421 +end 1.422 + 1.423 +filters.class = "mdl-special-card mdl-card__fullwidth mdl-shadow--2dp" 1.424 + 1.425 +ui.filters(filters) 1.426 + 1.427 + 1.428 +