]> git.proxmox.com Git - pmg-gui.git/blame - js/mobile/quarantineview.js
mobileui: fix list height
[pmg-gui.git] / js / mobile / quarantineview.js
CommitLineData
43f0b189
DC
1class QuarantineView extends Component {
2 constructor(config = {}) {
3 config.tpl = config.tpl || `
4 <div class="view view-quarantine">
5 <div data-name="quarantine-list" class="page">
6 <div class="navbar">
7 <div class="navbar-inner">
8 <div class="left">
9 <img class="logo-navbar" style="padding: 0 10px" src="pve2/images/logo-128.png" height=32 />
10 </div>
11 <div class="title">Mail Gateway</div>
12 </div>
13 </div>
14 <div class="settings-form elevation-5 fab-morph-target">
15 <div class="block-title block-title-medium">{{gettext "Range"}}</div>
16 <div class="list no-hairlines-md">
17 <ul>
18 <li class="item-content item-input">
19 <div class="item-inner">
20 <div class="item-title item-label">{{gettext "From"}}</div>
21 <div class="item-input-wrap">
22 <input type="date" name="from" placeholder="from" required validate>
23 </div>
24 </div>
25 </li>
26 <li class="item-content item-input">
27 <div class="item-inner">
28 <div class="item-title item-label">{{gettext "To"}}</div>
29 <div class="item-input-wrap">
30 <input type="date" name="to" placeholder="to" required validate>
31 </div>
32 </div>
33 </li>
34 </ul>
35 <a class="button fab-close range-form">{{gettext "OK"}}</a>
36 </div>
37 </div>
38 <div class="fab fab-morph fab-right-bottom" data-morph-to=".settings-form">
39 <a href="#">
40 <i class="icon f7-icons ios-only">calendar</i>
41 <i class="icon material-icons md-only">date_range</i>
42 </a>
43 </div>
44 <div class="toolbar subscription toolbar-hidden toolbar-bottom">
45 <div class="toolbar-inner">
46 <a class="button subscription">
47 <i class="icon f7-icons ios-only color-yellow">alert</i>
48 <i class="icon material-icons md-only color-yellow">warning</i>
49 <span class="subscription-text">
50 {{gettext "No valid subscription"}}
51 </span>
52 </a>
53 </div>
54 </div>
55 <div class="page-content ptr-content">
56 <div class="ptr-preloader">
57 <div class="preloader"></div>
58 <div class="ptr-arrow"></div>
59 </div>
60 <div class="list virtual-list"></div>
61 </div>
62 </div>
63 </div>`;
64 config.itemTemplate = config.itemTemplate || `
65 <li class="swipeout">
66 <div class="swipeout-content">
67 <a href="/mail/{{id}}/" class="item-link item-content">
68 <div class="item-inner">
69 <div class="item-title">
70 <div class="item-header">{{escape from}}</div>
71 {{escape subject}}
72 </div>
73 <div class="item-after">Score: {{js "this.spamlevel || 0"}}</div>
74 </div>
75 </a>
76 </div>
77 <div class="swipeout-actions-left">
78 <a href="/mail/{{id}}/deliver" class="color-green swipeout-close">
79 <i class="icon f7-icons ios-only">paper_plane</i>
80 <i class="icon material-icons md-only">send</i>
81 &nbsp;{{gettext "Deliver"}}
82 </a>
83 <a href="/mail/{{id}}/whitelist" class="swipeout-close">
84 <i class="icon f7-icons ios-only">check</i>
85 <i class="icon material-icons md-only">check</i>
86 &nbsp;{{gettext "Whitelist"}}
87 </a>
88 </div>
89 <div class="swipeout-actions-right">
90 <a href="/mail/{{id}}/blacklist" class="color-orange swipeout-close">
91 <i class="icon f7-icons ios-only">close</i>
92 <i class="icon material-icons md-only">close</i>
93 &nbsp;{{gettext "Blacklist"}}
94 </a>
95 <a href="/mail/{{id}}/delete" class="color-red swipeout-close">
96 <i class="icon f7-icons ios-only">trash</i>
97 <i class="icon material-icons md-only">delete</i>
98 &nbsp;{{gettext "Delete"}}
99 </a>
100 </div>
101 </li>`;
102 config.dividerTemplate = config.dividerTemplate ||
103 '<li class="item-divider">{{group}}</li>';
104 super(config);
105
106 var me = this;
107
108 me._compiledItemTemplate = Template7.compile(me.config.itemTemplate);
109 me._compiledDividerTemplate = Template7.compile(me.config.dividerTemplate);
110 me.skelTpl = `
111 <li class="skeleton-text skeleton-effect-fade">
112 <a href="#" class="item-content item-link">
113 <div class="item-inner">
114 <div class="item-title">
115 <div class="item-header">_______________________</div>
116 ____ ______ __ _______ ____ _______ _______ ___
117 </div>
118 <div class="item-after">Score: 15</div>
119 </div>
120 </a>
121 </li>`;
122 me.skelDividerTpl = '<li class="item-divider skeleton-text">____-__-__</li>';
123 me.setEndtime(new Date());
124 let startdate = new Date();
125 startdate.setDate(startdate.getDate() - 7);
126 me.setStarttime(startdate);
127
128 // add to dom
129 $$(me.config.target || '#app').append(me.getEl());
130
131 $$(document).on('page:init', '.page[data-name=quarantine-list]', (e, page) => {
132 me.vList = app.virtualList.create({
133 el: '.virtual-list',
134 items: [],
135 renderItem: function(item) {
136 return me._renderItem(item);
137 },
7a0d8540
DC
138 height: function(item) {
139 return me._calculateHeight(item);
140 },
43f0b189
DC
141 emptyTemplate: '<div class="empty">No data in database</div>'
142 });
143
144 // setup pull to refresh
145 $$('.ptr-content').on('ptr:refresh', (e) => {
146 me.setItems([
147 { skel: true, divider: true },
148 { skel: true },
149 { skel: true },
150 { skel: true },
151 { skel: true, divider: true },
152 { skel: true },
153 { skel: true },
154 { skel: true },
155 { skel: true },
156 { skel: true },
157 { skel: true },
158 { skel: true },
159 ]);
160 me.load().then(data => {
161 me.setItems(data, {
162 sorter: {
163 property: 'time',
164 numeric: true,
165 direction: 'DESC'
166 },
167 grouperFn: (val) => PMG.Utils.unixToIso(val['time'])
168 });
169 }).catch(PMG.Utils.showError).then(() => {
170 e.detail();
171 });
172 });
173
174 // process query parameters
175 let { mail, action, date, username, ticket } = PMG.Utils.extractParams();
176 if (date) {
177 me.setStarttime(date);
178 }
179
180 // setup range form
181 $$('input[name=from]').val(PMG.Utils.unixToIso(me.starttime));
182 $$('input[name=to]').val(PMG.Utils.unixToIso(me.endtime));
183
184 $$('.fab').on('fab:close', () => {
185 let fromChanged = me.setStarttime($$('input[name=from]').val());
186 let toChanged = me.setEndtime($$('input[name=to]').val());
187 if (fromChanged || toChanged) {
188 app.ptr.refresh();
189 }
190 });
191
192 // check login
193
194 let loginInfo = { username, ticket };
195 let showPopup = (username && ticket) || !PMG.Utils.authOK();
196 me._loginScreen = new LoginScreen({ loginInfo });
197
198 me._loginScreen.open().then(data => {
199 me._loginScreen.close();
200 PMG.Utils.setLoginInfo(data);
201 return PMG.Utils.getSubscriptionInfo();
202 }).then(data => {
203 return PMG.Utils.checkSubscription(data, showPopup);
204 }).then(data => {
205 app.ptr.refresh();
206 if (mail) {
207 let url = "/mail/" + mail + "/" + (action || "");
208 me._view.router.navigate(url);
209 }
210 }).catch(PMG.Utils.showError);
211 });
212
213 me._view = app.views.create('.view-quarantine', {
214 main: me.config.mainView !== undefined ? me.config.mainView : true,
215 url: '/',
216 pushState: true,
217 pushStateAnimateOnLoad: true
218 });
219 }
220 setStarttime(starttime) {
221 var me = this;
222 let date = starttime;
223 if (!(starttime instanceof Date)) {
224 // we assume an ISO string
225 if (starttime == '') {
226 return;
227 }
228 date = new Date(PMG.Utils.isoToUnix(starttime)*1000);
229 }
230 // starttime is at beginning of date
231 date.setHours(0,0,0,0);
232 let result = Math.round(date.getTime()/1000);
233 if (result !== me.starttime) {
234 me.starttime = result;
235 return true;
236 }
237 return false
238 }
239 setEndtime(endtime) {
240 var me = this;
241 let date = endtime;
242 if (!(endtime instanceof Date)) {
243 if (endtime == '') {
244 return;
245 }
246 // we assume an ISO string
247 date = new Date(PMG.Utils.isoToUnix(endtime)*1000);
248 }
249 // endtime is at the end of the day
250 date.setHours(23, 59, 59);
251 let result = Math.round(date.getTime()/1000);
252 if (result !== me.endtime) {
253 me.endtime = result;
254 return true;
255 }
256 return false;
257 }
7a0d8540
DC
258 _calculateHeight(item) {
259 var me = this;
260
261 let height = 48; // default
262
263 if (typeof item === 'object') {
264 let type = app.theme + '-' + (item.divider? "divider" : 'item');
265 switch (type) {
266 case 'md-divider':
267 height = 48;
268 break;
269 case 'md-item':
270 height = 54;
271 break;
272 case 'ios-divider':
273 height = 31;
274 break;
275 case 'ios-item':
276 height = 53;
277 break;
278 default: ;
279 }
280 }
281
282 return height;
283 }
43f0b189
DC
284 _renderItem(item) {
285 var me = this;
286
287 if(typeof item === 'object') {
288 if (item.skel) {
289 return item.divider? me.skelDividerTpl : me.skelTpl;
290 } else if (item.divider) {
291 return me._compiledDividerTemplate(item);
292 } else {
293 return me._compiledItemTemplate(item);
294 }
295 }
296
297 return item.toString();
298 }
299 setItems(items, options) {
300 var me = this;
301 if (options && options.sorter) {
302 if (options.sorter.sorterFn) {
303 items.sort(options.sorter.sorterFn);
304 } else {
305 let prop = options.sorter.property;
306 let numeric = options.sorter.numeric;
307 let dir = options.sorter.direction === "ASC" ? 1 : -1;
308 items.sort((a,b) => {
309 let result;
310
311 if (numeric) {
312 result = a[prop] - b[prop];
313 } else {
314 result = a[prop] === b[prop] ? 0 : (a[prop] < b[prop] ? 1 : -1);
315 }
316
317 return result * dir;
318 });
319 }
320 }
321 me.vList.replaceAllItems(items);
322 if (options && options.grouperFn) {
323 let lastgroup;
324 let offset = 0;
325 for (let i = 0; i+offset < items.length; i++) {
326 let item = items[i+offset];
327 let curgroup = options.grouperFn(item);
328 if (curgroup != lastgroup) {
329 me.vList.insertItemBefore(i+(offset++), {
330 divider: true,
331 group: curgroup
332 });
333 lastgroup = curgroup;
334 }
335 }
336 }
337 }
338 load() {
339 var me = this;
340 return new Promise(function(resolve, reject) {
341 app.request({
342 url: '/api2/json/quarantine/spam',
343 data: {
344 starttime: me.starttime,
345 endtime: me.endtime
346 },
347 dataType: 'json',
348 success: (response, status, xhr) => {
349 resolve(response.data);
350 },
351 error: xhr => {
352 reject(xhr);
353 }
354 });
355 });
356 }
357}
358