]> git.proxmox.com Git - pmg-gui.git/blob - js/mobile/quarantineview.js
close #1671: implement mobile UI for quarantine
[pmg-gui.git] / js / mobile / quarantineview.js
1 class 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 },
138 emptyTemplate: '<div class="empty">No data in database</div>'
139 });
140
141 // setup pull to refresh
142 $$('.ptr-content').on('ptr:refresh', (e) => {
143 me.setItems([
144 { skel: true, divider: true },
145 { skel: true },
146 { skel: true },
147 { skel: true },
148 { skel: true, divider: true },
149 { skel: true },
150 { skel: true },
151 { skel: true },
152 { skel: true },
153 { skel: true },
154 { skel: true },
155 { skel: true },
156 ]);
157 me.load().then(data => {
158 me.setItems(data, {
159 sorter: {
160 property: 'time',
161 numeric: true,
162 direction: 'DESC'
163 },
164 grouperFn: (val) => PMG.Utils.unixToIso(val['time'])
165 });
166 }).catch(PMG.Utils.showError).then(() => {
167 e.detail();
168 });
169 });
170
171 // process query parameters
172 let { mail, action, date, username, ticket } = PMG.Utils.extractParams();
173 if (date) {
174 me.setStarttime(date);
175 }
176
177 // setup range form
178 $$('input[name=from]').val(PMG.Utils.unixToIso(me.starttime));
179 $$('input[name=to]').val(PMG.Utils.unixToIso(me.endtime));
180
181 $$('.fab').on('fab:close', () => {
182 let fromChanged = me.setStarttime($$('input[name=from]').val());
183 let toChanged = me.setEndtime($$('input[name=to]').val());
184 if (fromChanged || toChanged) {
185 app.ptr.refresh();
186 }
187 });
188
189 // check login
190
191 let loginInfo = { username, ticket };
192 let showPopup = (username && ticket) || !PMG.Utils.authOK();
193 me._loginScreen = new LoginScreen({ loginInfo });
194
195 me._loginScreen.open().then(data => {
196 me._loginScreen.close();
197 PMG.Utils.setLoginInfo(data);
198 return PMG.Utils.getSubscriptionInfo();
199 }).then(data => {
200 return PMG.Utils.checkSubscription(data, showPopup);
201 }).then(data => {
202 app.ptr.refresh();
203 if (mail) {
204 let url = "/mail/" + mail + "/" + (action || "");
205 me._view.router.navigate(url);
206 }
207 }).catch(PMG.Utils.showError);
208 });
209
210 me._view = app.views.create('.view-quarantine', {
211 main: me.config.mainView !== undefined ? me.config.mainView : true,
212 url: '/',
213 pushState: true,
214 pushStateAnimateOnLoad: true
215 });
216 }
217 setStarttime(starttime) {
218 var me = this;
219 let date = starttime;
220 if (!(starttime instanceof Date)) {
221 // we assume an ISO string
222 if (starttime == '') {
223 return;
224 }
225 date = new Date(PMG.Utils.isoToUnix(starttime)*1000);
226 }
227 // starttime is at beginning of date
228 date.setHours(0,0,0,0);
229 let result = Math.round(date.getTime()/1000);
230 if (result !== me.starttime) {
231 me.starttime = result;
232 return true;
233 }
234 return false
235 }
236 setEndtime(endtime) {
237 var me = this;
238 let date = endtime;
239 if (!(endtime instanceof Date)) {
240 if (endtime == '') {
241 return;
242 }
243 // we assume an ISO string
244 date = new Date(PMG.Utils.isoToUnix(endtime)*1000);
245 }
246 // endtime is at the end of the day
247 date.setHours(23, 59, 59);
248 let result = Math.round(date.getTime()/1000);
249 if (result !== me.endtime) {
250 me.endtime = result;
251 return true;
252 }
253 return false;
254 }
255 _renderItem(item) {
256 var me = this;
257
258 if(typeof item === 'object') {
259 if (item.skel) {
260 return item.divider? me.skelDividerTpl : me.skelTpl;
261 } else if (item.divider) {
262 return me._compiledDividerTemplate(item);
263 } else {
264 return me._compiledItemTemplate(item);
265 }
266 }
267
268 return item.toString();
269 }
270 setItems(items, options) {
271 var me = this;
272 if (options && options.sorter) {
273 if (options.sorter.sorterFn) {
274 items.sort(options.sorter.sorterFn);
275 } else {
276 let prop = options.sorter.property;
277 let numeric = options.sorter.numeric;
278 let dir = options.sorter.direction === "ASC" ? 1 : -1;
279 items.sort((a,b) => {
280 let result;
281
282 if (numeric) {
283 result = a[prop] - b[prop];
284 } else {
285 result = a[prop] === b[prop] ? 0 : (a[prop] < b[prop] ? 1 : -1);
286 }
287
288 return result * dir;
289 });
290 }
291 }
292 me.vList.replaceAllItems(items);
293 if (options && options.grouperFn) {
294 let lastgroup;
295 let offset = 0;
296 for (let i = 0; i+offset < items.length; i++) {
297 let item = items[i+offset];
298 let curgroup = options.grouperFn(item);
299 if (curgroup != lastgroup) {
300 me.vList.insertItemBefore(i+(offset++), {
301 divider: true,
302 group: curgroup
303 });
304 lastgroup = curgroup;
305 }
306 }
307 }
308 }
309 load() {
310 var me = this;
311 return new Promise(function(resolve, reject) {
312 app.request({
313 url: '/api2/json/quarantine/spam',
314 data: {
315 starttime: me.starttime,
316 endtime: me.endtime
317 },
318 dataType: 'json',
319 success: (response, status, xhr) => {
320 resolve(response.data);
321 },
322 error: xhr => {
323 reject(xhr);
324 }
325 });
326 });
327 }
328 }
329