diff options
author | Johannes Zellner <johannes@cloudron.io> | 2018-05-18 13:31:57 +0200 |
---|---|---|
committer | Johannes Zellner <johannes@cloudron.io> | 2018-05-18 13:31:57 +0200 |
commit | 25c2c5dea457b206c26a9f746210016773a919b3 (patch) | |
tree | 5939979eb86ccd5f3da922523f5814b1c407544c /frontend | |
parent | 5e3bdc8079acd29f95fdbe81c3212791e9218777 (diff) | |
download | Surfer-25c2c5dea457b206c26a9f746210016773a919b3.tar.gz Surfer-25c2c5dea457b206c26a9f746210016773a919b3.tar.zst Surfer-25c2c5dea457b206c26a9f746210016773a919b3.zip |
Improve error feedback
Diffstat (limited to 'frontend')
-rw-r--r-- | frontend/index.html | 7 | ||||
-rw-r--r-- | frontend/js/app.js | 78 |
2 files changed, 47 insertions, 38 deletions
diff --git a/frontend/index.html b/frontend/index.html index 321801b..5856a16 100644 --- a/frontend/index.html +++ b/frontend/index.html | |||
@@ -25,12 +25,12 @@ | |||
25 | 25 | ||
26 | <el-dialog title="Login" :visible.sync="ready && !session.valid" width="30%" :close-on-press-escape="false" :show-close="false"> | 26 | <el-dialog title="Login" :visible.sync="ready && !session.valid" width="30%" :close-on-press-escape="false" :show-close="false"> |
27 | <el-form :model="loginData" label-position="top" @submit.native.prevent> | 27 | <el-form :model="loginData" label-position="top" @submit.native.prevent> |
28 | <el-form-item label="Username"><el-input v-model="loginData.username" required autofocus></el-input></el-form-item> | 28 | <el-form-item label="Username"><el-input v-model="loginData.username" required autofocus :disabled="loginData.busy"></el-input></el-form-item> |
29 | <el-form-item label="Password"><el-input v-model="loginData.password" type="password" required></el-input></el-form-item> | 29 | <el-form-item label="Password"><el-input v-model="loginData.password" type="password" required :disabled="loginData.busy"></el-input></el-form-item> |
30 | <input type="submit" @click="onLogin" v-show="false"/> | 30 | <input type="submit" @click="onLogin" v-show="false"/> |
31 | </el-form> | 31 | </el-form> |
32 | <span slot="footer" class="dialog-footer"> | 32 | <span slot="footer" class="dialog-footer"> |
33 | <el-button type="primary" @click="onLogin"><i class="el-icon-loading" v-show="busy"></i><span v-show="!busy">Login</span></el-button> | 33 | <el-button type="primary" @click="onLogin"><i class="el-icon-loading" v-show="loginData.busy"></i><span v-show="!loginData.busy">Login</span></el-button> |
34 | </span> | 34 | </span> |
35 | </el-dialog> | 35 | </el-dialog> |
36 | 36 | ||
@@ -53,7 +53,6 @@ | |||
53 | <el-button-group> | 53 | <el-button-group> |
54 | <el-button type="primary" icon="el-icon-upload" size="small" @click="onUpload">Upload</el-button> | 54 | <el-button type="primary" icon="el-icon-upload" size="small" @click="onUpload">Upload</el-button> |
55 | <el-button type="primary" icon="el-icon-plus" size="small" @click="onNewFolder">New Folder</el-button> | 55 | <el-button type="primary" icon="el-icon-plus" size="small" @click="onNewFolder">New Folder</el-button> |
56 | |||
57 | </el-button-group> | 56 | </el-button-group> |
58 | <el-dropdown @command="onOptionsMenu"> | 57 | <el-dropdown @command="onOptionsMenu"> |
59 | <el-button size="small" icon="el-icon-more"></el-button> | 58 | <el-button size="small" icon="el-icon-more"></el-button> |
diff --git a/frontend/js/app.js b/frontend/js/app.js index 6303a89..92dc1de 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js | |||
@@ -19,10 +19,7 @@ function asyncForEach(items, handler, callback) { | |||
19 | } | 19 | } |
20 | 20 | ||
21 | function getProfile(accessToken, callback) { | 21 | function getProfile(accessToken, callback) { |
22 | callback = callback || function (error) { if (error) console.error(error); }; | ||
23 | |||
24 | superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) { | 22 | superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) { |
25 | app.busy = false; | ||
26 | app.ready = true; | 23 | app.ready = true; |
27 | 24 | ||
28 | if (error && !error.response) return callback(error); | 25 | if (error && !error.response) return callback(error); |
@@ -179,7 +176,7 @@ var app = new Vue({ | |||
179 | el: '#app', | 176 | el: '#app', |
180 | data: { | 177 | data: { |
181 | ready: false, | 178 | ready: false, |
182 | busy: true, | 179 | busy: false, |
183 | uploadStatus: { | 180 | uploadStatus: { |
184 | busy: false, | 181 | busy: false, |
185 | count: 0, | 182 | count: 0, |
@@ -194,19 +191,22 @@ var app = new Vue({ | |||
194 | folderListingEnabled: false, | 191 | folderListingEnabled: false, |
195 | loginData: { | 192 | loginData: { |
196 | username: '', | 193 | username: '', |
197 | password: '' | 194 | password: '', |
195 | busy: false | ||
198 | }, | 196 | }, |
199 | entries: [] | 197 | entries: [] |
200 | }, | 198 | }, |
201 | methods: { | 199 | methods: { |
202 | onLogin: function () { | 200 | onLogin: function () { |
203 | app.busy = true; | 201 | var that = this; |
204 | 202 | ||
205 | superagent.post('/api/login').send({ username: app.loginData.username, password: app.loginData.password }).end(function (error, result) { | 203 | that.loginData.busy = true; |
206 | app.busy = false; | ||
207 | 204 | ||
208 | if (error) return console.error(error); | 205 | superagent.post('/api/login').send({ username: that.loginData.username, password: that.loginData.password }).end(function (error, result) { |
209 | if (result.statusCode === 401) return console.error('Invalid credentials'); | 206 | that.loginData.busy = false; |
207 | |||
208 | if (error && !result) return that.$message.error(error.message); | ||
209 | if (result.statusCode === 401) return that.$message.error('Wrong username or password'); | ||
210 | 210 | ||
211 | getProfile(result.body.accessToken, function (error) { | 211 | getProfile(result.body.accessToken, function (error) { |
212 | if (error) return console.error(error); | 212 | if (error) return console.error(error); |
@@ -216,6 +216,8 @@ var app = new Vue({ | |||
216 | }); | 216 | }); |
217 | }, | 217 | }, |
218 | onOptionsMenu: function (command) { | 218 | onOptionsMenu: function (command) { |
219 | var that = this; | ||
220 | |||
219 | if (command === 'folderListing') { | 221 | if (command === 'folderListing') { |
220 | superagent.put('/api/settings').send({ folderListingEnabled: this.folderListingEnabled }).query({ access_token: localStorage.accessToken }).end(function (error) { | 222 | superagent.put('/api/settings').send({ folderListingEnabled: this.folderListingEnabled }).query({ access_token: localStorage.accessToken }).end(function (error) { |
221 | if (error) console.error(error); | 223 | if (error) console.error(error); |
@@ -234,7 +236,7 @@ var app = new Vue({ | |||
234 | superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) { | 236 | superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) { |
235 | if (error) console.error(error); | 237 | if (error) console.error(error); |
236 | 238 | ||
237 | app.session.valid = false; | 239 | that.session.valid = false; |
238 | 240 | ||
239 | delete localStorage.accessToken; | 241 | delete localStorage.accessToken; |
240 | }); | 242 | }); |
@@ -242,30 +244,34 @@ var app = new Vue({ | |||
242 | }, | 244 | }, |
243 | onDownload: function (entry) { | 245 | onDownload: function (entry) { |
244 | if (entry.isDirectory) return; | 246 | if (entry.isDirectory) return; |
245 | window.location.href = encode('/api/files/' + sanitize(app.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken; | 247 | window.location.href = encode('/api/files/' + sanitize(this.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken; |
246 | }, | 248 | }, |
247 | onUpload: function () { | 249 | onUpload: function () { |
248 | $(app.$refs.upload).on('change', function () { | 250 | var that = this; |
251 | |||
252 | $(this.$refs.upload).on('change', function () { | ||
249 | 253 | ||
250 | // detach event handler | 254 | // detach event handler |
251 | $(app.$refs.upload).off('change'); | 255 | $(that.$refs.upload).off('change'); |
252 | 256 | ||
253 | uploadFiles(app.$refs.upload.files || []); | 257 | uploadFiles(that.$refs.upload.files || []); |
254 | }); | 258 | }); |
255 | 259 | ||
256 | // reset the form first to make the change handler retrigger even on the same file selected | 260 | // reset the form first to make the change handler retrigger even on the same file selected |
257 | app.$refs.upload.value = ''; | 261 | this.$refs.upload.value = ''; |
258 | app.$refs.upload.click(); | 262 | this.$refs.upload.click(); |
259 | }, | 263 | }, |
260 | onDelete: function (entry) { | 264 | onDelete: function (entry) { |
265 | var that = this; | ||
266 | |||
261 | var title = 'Really delete ' + (entry.isDirectory ? 'folder ' : '') + entry.filePath; | 267 | var title = 'Really delete ' + (entry.isDirectory ? 'folder ' : '') + entry.filePath; |
262 | this.$confirm('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No' }).then(function () { | 268 | this.$confirm('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No' }).then(function () { |
263 | var path = encode(sanitize(app.path + '/' + entry.filePath)); | 269 | var path = encode(sanitize(that.path + '/' + entry.filePath)); |
264 | 270 | ||
265 | superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) { | 271 | superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) { |
266 | if (result && result.statusCode === 401) return logout(); | 272 | if (result && result.statusCode === 401) return logout(); |
267 | if (result && result.statusCode !== 200) return console.error('Error deleting file: ', result.statusCode); | 273 | if (result && result.statusCode !== 200) return that.$message.error('Error deleting file: ' + result.statusCode); |
268 | if (error) return console.error(error); | 274 | if (error) return that.$message.error(error.message); |
269 | 275 | ||
270 | refresh(); | 276 | refresh(); |
271 | }); | 277 | }); |
@@ -274,38 +280,42 @@ var app = new Vue({ | |||
274 | }); | 280 | }); |
275 | }, | 281 | }, |
276 | onRename: function (entry) { | 282 | onRename: function (entry) { |
283 | var that = this; | ||
284 | |||
277 | var title = 'Rename ' + entry.filePath; | 285 | var title = 'Rename ' + entry.filePath; |
278 | this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new filename', inputValue: entry.filePath }).then(function (data) { | 286 | this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new filename', inputValue: entry.filePath }).then(function (data) { |
279 | var path = encode(sanitize(app.path + '/' + entry.filePath)); | 287 | var path = encode(sanitize(that.path + '/' + entry.filePath)); |
280 | var newFilePath = sanitize(app.path + '/' + data.value); | 288 | var newFilePath = sanitize(that.path + '/' + data.value); |
281 | 289 | ||
282 | superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) { | 290 | superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) { |
283 | if (result && result.statusCode === 401) return logout(); | 291 | if (result && result.statusCode === 401) return logout(); |
284 | if (result && result.statusCode !== 200) return console.error('Error renaming file: ', result.statusCode); | 292 | if (result && result.statusCode !== 200) return that.$message.error('Error renaming file: ' + result.statusCode); |
285 | if (error) return console.error(error); | 293 | if (error) return that.$message.error(error.message); |
286 | 294 | ||
287 | refresh(); | 295 | refresh(); |
288 | }); | 296 | }); |
289 | }).catch(function () { | 297 | }).catch(function (error) { |
290 | console.log('rename error:', arguments); | 298 | that.$message.error(error.message); |
291 | }); | 299 | }); |
292 | }, | 300 | }, |
293 | onNewFolder: function () { | 301 | onNewFolder: function () { |
302 | var that = this; | ||
303 | |||
294 | var title = 'Create New Folder'; | 304 | var title = 'Create New Folder'; |
295 | this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new foldername' }).then(function (data) { | 305 | this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new foldername' }).then(function (data) { |
296 | var path = encode(sanitize(app.path + '/' + data.value)); | 306 | var path = encode(sanitize(that.path + '/' + data.value)); |
297 | 307 | ||
298 | superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) { | 308 | superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) { |
299 | if (result && result.statusCode === 401) return logout(); | 309 | if (result && result.statusCode === 401) return logout(); |
300 | if (result && result.statusCode === 403) return console.error('Name not allowed'); | 310 | if (result && result.statusCode === 403) return that.$message.error('Folder name not allowed'); |
301 | if (result && result.statusCode === 409) return console.error('Directory already exists'); | 311 | if (result && result.statusCode === 409) return that.$message.error('Folder already exists'); |
302 | if (result && result.statusCode !== 201) return console.error('Error creating directory: ', result.statusCode); | 312 | if (result && result.statusCode !== 201) return that.$message.error('Error creating folder: ' + result.statusCode); |
303 | if (error) return console.error(error); | 313 | if (error) return that.$message.error(error.message); |
304 | 314 | ||
305 | refresh(); | 315 | refresh(); |
306 | }); | 316 | }); |
307 | }).catch(function () { | 317 | }).catch(function (error) { |
308 | console.log('create folder error:', arguments); | 318 | that.$message.error(error.message); |
309 | }); | 319 | }); |
310 | }, | 320 | }, |
311 | prettyDate: function (row, column, cellValue, index) { | 321 | prettyDate: function (row, column, cellValue, index) { |
@@ -333,7 +343,7 @@ var app = new Vue({ | |||
333 | }, | 343 | }, |
334 | loadDirectory: loadDirectory, | 344 | loadDirectory: loadDirectory, |
335 | onUp: function () { | 345 | onUp: function () { |
336 | window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/')); | 346 | window.location.hash = sanitize(this.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/')); |
337 | }, | 347 | }, |
338 | open: open, | 348 | open: open, |
339 | drop: drop, | 349 | drop: drop, |