liquid_feedback_frontend

view app/main/issue/_list.lua @ 1590:8b3375b7f590

Fixed invalid condition for showing action element in issues
author bsw
date Wed Jan 27 10:07:05 2021 +0100 (2021-01-27)
parents 1c215ddaf09a
children a3a7120a67be
line source
1 local for_member = param.get ( "for_member", "table" )
2 local for_unit = param.get ( "for_unit", "table" )
3 local for_area = param.get ( "for_area", "table" )
4 local for_issue = param.get ( "for_issue", "table" )
5 local for_initiative = param.get ( "for_initiative", "table" )
6 local for_sidebar = param.get("for_sidebar", atom.boolean)
7 local no_filter = param.get ( "no_filter", atom.boolean )
8 local search = param.get ( "search" )
10 local limit = 25
12 local mode = request.get_param{ name = "mode" } or "issue"
14 if for_initiative or for_issue or for_member then
15 mode = "timeline"
16 end
18 if config.single_unit_id then
19 for_unit = Unit:by_id(config.single_unit_id)
20 end
22 local selector
24 if search then
26 selector = Issue:get_search_selector(search)
29 elseif mode == "timeline" then
31 local event_max_id = request.get_param_strings()["event_max_id"]
33 selector = Event:new_selector()
34 :add_order_by("event.id DESC")
35 :join("issue", nil, "issue.id = event.issue_id")
36 :add_field("now() - event.occurrence", "time_ago")
37 :limit(limit + 1)
39 if event_max_id then
40 selector:add_where{ "event.id < ?", event_max_id }
41 end
43 if for_member then
44 selector:add_where{ "event.member_id = ?", for_member.id }
45 end
47 if for_initiative then
48 selector:add_where{ "event.initiative_id = ?", for_initiative.id }
49 end
52 elseif mode == "issue" then
54 selector = Issue:new_selector()
56 end
58 if for_unit then
59 selector:join("area", nil, "area.id = issue.area_id")
60 selector:add_where{ "area.unit_id = ?", for_unit.id }
61 elseif for_area then
62 selector:add_where{ "issue.area_id = ?", for_area.id }
63 elseif for_issue then
64 selector:add_where{ "issue.id = ?", for_issue.id }
65 end
67 if not search and app.session.member_id then
68 selector
69 :left_join("interest", "_interest", {
70 "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id
71 } )
72 :add_field("(_interest.member_id NOTNULL)", "is_interested")
73 :left_join("delegating_interest_snapshot", "_delegating_interest", { [[
74 _delegating_interest.issue_id = issue.id AND
75 _delegating_interest.member_id = ? AND
76 _delegating_interest.snapshot_id = issue.latest_snapshot_id
77 ]], app.session.member.id } )
78 :add_field("_delegating_interest.delegate_member_ids[1]", "is_interested_by_delegation_to_member_id")
79 :add_field("_delegating_interest.delegate_member_ids[array_upper(_delegating_interest.delegate_member_ids, 1)]", "is_interested_via_member_id")
80 :add_field("array_length(_delegating_interest.delegate_member_ids, 1)", "delegation_chain_length")
81 end
83 local function doit()
85 local last_event_id
87 local items = selector:exec()
89 local row_class = "sectionRow"
90 if for_sidebar then
91 row_class = "sidebarRow"
92 end
94 if mode == "timeline" then
95 local issues = items:load ( "issue" )
96 local initiative = items:load ( "initiative" )
97 items:load ( "suggestion" )
98 items:load ( "member" )
99 issues:load_everything_for_member_id ( app.session.member_id )
100 initiative:load_everything_for_member_id ( app.session.member_id )
101 elseif mode == "issue" then
102 items:load_everything_for_member_id ( app.session.member_id )
103 end
105 local last_event_date
106 for i, item in ipairs(items) do
107 local event
108 local issue
109 if mode == "timeline" then
110 event = item
111 issue = item.issue
112 elseif mode == "issue" then
113 event = {}
114 issue = item
115 end
117 last_event_id = event.id
119 local class = "mdl-card mdl-shadow--2dp mdl-card__fullwidth event " .. row_class
120 if event.suggestion_id then
121 class = class .. " suggestion"
122 end
124 ui.container{ attr = { class = class, }, content = function()
125 local event_name
126 local event_icon
127 local negative_event = false
129 local days_ago_text
131 if mode == "timeline" then
132 event_name = event.event_name
133 if event.event == "issue_state_changed" then
134 if event.state == "discussion" then
135 event_name = _"Discussion started"
136 elseif event.state == "verification" then
137 event_name = _"Verification started"
138 elseif event.state == "voting" then
139 event_name = _"Voting started"
140 elseif event.state == "finished_with_winner" then
141 event_name = event.state_name
142 elseif event.state == "finished_without_winner" then
143 event_name = event.state_name
144 negative_event = true
145 else
146 event_name = event.state_name
147 negative_event = true
148 end
149 elseif event.event == "initiative_revoked" then
150 negative_event = true
151 end
153 if event.time_ago == 0 then
154 days_ago_text = _("today at #{time}", { time = format.time(event.occurrence) })
155 elseif event.time_ago == 1 then
156 days_ago_text = _("yesterday at #{time}", { time = format.time(event.occurrence) })
157 else
158 days_ago_text = _("#{interval} ago", { interval = format.interval_text ( event.time_ago ) } )
159 end
161 elseif mode == "issue" then
162 local event_icons_map = {
163 admission = "bubble_chart",
164 discussion = "question_answer",
165 verification = "find_in_page",
166 voting = "mail",
167 finished_with_winner = "gavel",
168 finished_without_winner = "gavel",
169 canceled = "clear"
170 }
171 event_icon = event_icons_map[issue.state] or event_icons_map["canceled"]
172 event_name = issue.state_name
173 if issue.state_time_left:sub(1,1) ~= "-" then
174 days_ago_text = _( "#{interval_text} left", {
175 interval_text = format.interval_text ( issue.state_time_left )
176 })
177 elseif issue.closed then
178 days_ago_text = _( "#{interval_text} ago", {
179 interval_text = format.interval_text ( issue.closed_ago )
180 })
181 else
182 days_ago_text = _"phase ends soon"
183 end
184 if issue.closed and not issue.fully_frozen then
185 negative_event = true
186 end
187 if issue.state == "finished_without_winner"
188 or issue.state == "canceled_no_initiative_admitted"
189 or issue.state == "canceled_by_admin"
190 then
191 negative_event = true
192 end
193 end
195 local class= "event_info"
197 if negative_event then
198 class = class .. " negative"
199 end
201 if not for_issue and not for_initiative then
202 ui.container{ attr = { class = "mdl-card__title mdl-card--has-fab mdl-card--border card-issue" }, content = function()
203 ui.container{ attr = { class = "contextlinks" }, content = function()
204 if not (config.single_unit_id and config.single_area_id) then
205 if not config.single_unit_id then
206 slot.put ( " " )
207 ui.link{
208 module = "index", view = "index", params = { unit = issue.area.unit_id },
209 attr = { class = "unit" }, content = issue.area.unit.name
210 }
211 end
212 if not config.single_area_id then
213 if not config.single_unit_id then
214 slot.put(" » ")
215 end
216 ui.link{
217 module = "index", view = "index", params = { unit = issue.area.unit_id, area = issue.area_id },
218 attr = { class = "area" }, content = issue.area.name
219 }
220 end
221 end
222 slot.put(" » ")
223 ui.link{
224 module = "issue", view = "show", id = issue.id,
225 attr = { class = "issue" }, content = issue.name
226 }
227 -- end }
228 -- ui.container{ attr = { class = "mdl-card__subtitle-text .mdl-cell--hide-phone" }, content = function()
229 ui.container{ attr = { class = class, style = "float: right; color: #fff;" }, content = function ()
230 if event_icon then
231 ui.tag{ tag = "i", attr = { class = "material-icons", ["aria-hidden"] = "true" }, content = event_icon }
232 end
233 slot.put(" ")
234 ui.tag { content = event_name }
235 slot.put(" ")
236 ui.tag{ content = "(" .. days_ago_text .. ")" }
237 end }
238 end }
239 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
240 ui.link {
241 attr = { class = "mdl-button mdl-js-button mdl-button--fab mdl-button--colored" ,
242 style = "position: absolute; right: 20px; bottom: -27px;"
243 },
244 module = "vote", view = "list",
245 params = { issue_id = issue.id },
246 content = function()
247 ui.tag{ tag = "i", attr = { class = "material-icons" }, content = config.voting_icon or "mail_outline" }
248 end
249 }
250 end
251 end }
252 end
254 if event.suggestion_id then
255 ui.container{ attr = { class = "suggestion" }, content = function()
256 ui.link{
257 text = format.string(event.suggestion.name, {
258 truncate_at = 160, truncate_suffix = true
259 }),
260 module = "initiative", view = "show", id = event.initiative.id,
261 params = { suggestion_id = event.suggestion_id },
262 anchor = "s" .. event.suggestion_id
263 }
264 end }
265 end
267 if not for_initiative and (not for_issue or event.initiative_id) then
269 ui.container{ attr = { class = "initiative_list" }, content = function()
270 if event.initiative_id then
271 local initiative = event.initiative
273 execute.view{ module = "initiative", view = "_list", params = {
274 issue = issue,
275 initiative = initiative,
276 for_event = mode == "timeline" and not (event.state == issue.state)
278 } }
279 else
280 local initiatives = issue.initiatives
281 execute.view{ module = "initiative", view = "_list", params = {
282 issue = issue,
283 initiatives = initiatives,
284 for_event = mode == "timeline" and not (event.state == issue.state)
285 } }
286 end
287 end }
288 end
289 if
290 app.session.member_id and (
291 (not issue.fully_frozen and app.session.member:has_initiative_right_for_unit_id(issue.area.unit_id))
292 or (issue.fully_frozen and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id))
293 )
294 then
295 ui.container{ attr = { class = "mdl-card__actions mdl-card--border" }, content = function()
296 execute.view{
297 module = "delegation", view = "_info", params = {
298 issue = issue, member = for_member
299 }
300 }
301 end }
302 end
303 end }
305 end
307 if mode == "timeline" then
308 if for_sidebar then
309 ui.container { attr = { class = row_class }, content = function ()
310 ui.link{
311 attr = { class = "moreLink" },
312 text = _"Show full history",
313 module = "initiative", view = "history", id = for_initiative.id
314 }
315 end }
316 elseif #items > limit then
317 ui.container { attr = { class = row_class }, content = function ()
318 local params = request.get_param_strings()
319 ui.link{
320 attr = { class = "moreLink" },
321 text = _"Show older events",
322 module = request.get_module(),
323 view = request.get_view(),
324 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,
325 params = {
326 mode = "timeline",
327 event_max_id = last_event_id,
328 tab = params["tab"],
329 phase = params["phase"],
330 closed = params["closed"]
331 }
332 }
333 end }
334 elseif #items < 1 then
335 ui.container { attr = { class = row_class }, content = _"No more events available" }
336 end
337 end
339 if #items < 1 then
340 ui.section( function()
341 ui.sectionRow( function()
342 ui.container{ content = _"No results for this selection" }
343 end )
344 end )
345 end
348 end
351 local filters = {}
353 if not for_initiative and not for_issue and not no_filter then
355 filters = execute.chunk{
356 module = "issue", chunk = "_filters", params = {
357 for_events = mode == "timeline" and true or false,
358 member = app.session.member,
359 for_member = for_member,
360 state = for_state,
361 for_unit = for_unit and true or false,
362 for_area = for_area and true or false
363 }}
364 end
366 filters.opened = true
367 filters.selector = selector
370 local function dotabs()
371 slot.select("filter", function()
372 ui.container{ attr = { class = "mdl-tabs mdl-js-tabs mdl-js-ripple-effect float-left" }, content = function()
373 ui.container{ attr = { class = "mdl-tabs__tab-bar" }, content = function()
374 local mode = request.get_param{ name = "mode" }
375 local css_active = (not mode or mode == "issue") and " is-active" or ""
376 ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), content = "Issues", attr = { class = "mdl-tabs__tab" .. css_active } }
377 local css_active = mode and " is-active" or " "
378 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 } }
379 ui.link{ module = "member", view = "list", content = "Member", attr = { class = "mdl-tabs__tab" } }
380 end }
381 end }
382 end)
383 end
386 if mode == "timeline" then
387 --dotabs()
388 filters.content = function()
389 execute.view{ module = "index", view = "_head" }
390 doit()
391 end
392 else
393 -- dotabs()
394 filters.content = function()
395 if config.voting_only then
396 local admission_order_field = "filter_issue_order.order_in_unit"
397 if for_area then
398 admission_order_field = "filter_issue_order.order_in_area"
399 end
400 selector:left_join ( "issue_order_in_admission_state", "filter_issue_order", "filter_issue_order.id = issue.id" )
401 selector:add_order_by ( "issue.closed DESC NULLS FIRST" )
402 selector:add_order_by ( "issue.accepted ISNULL" )
403 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" )
404 selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN " .. admission_order_field .. " ELSE NULL END" )
405 selector:add_order_by ( "id" )
406 end
407 if not search then
408 execute.view{ module = "index", view = "_head" }
409 end
410 ui.paginate{
411 selector = selector,
412 per_page = 25,
413 content = doit
414 }
415 end
416 end
418 filters.class = "mdl-special-card mdl-card__fullwidth mdl-shadow--2dp"
420 filters.legend = _"Filter issues:"
422 ui.filters(filters)

Impressum / About Us