aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
authorJohannes Zellner <johannes@cloudron.io>2018-05-18 13:31:57 +0200
committerJohannes Zellner <johannes@cloudron.io>2018-05-18 13:31:57 +0200
commit25c2c5dea457b206c26a9f746210016773a919b3 (patch)
tree5939979eb86ccd5f3da922523f5814b1c407544c /frontend
parent5e3bdc8079acd29f95fdbe81c3212791e9218777 (diff)
downloadSurfer-25c2c5dea457b206c26a9f746210016773a919b3.tar.gz
Surfer-25c2c5dea457b206c26a9f746210016773a919b3.tar.zst
Surfer-25c2c5dea457b206c26a9f746210016773a919b3.zip
Improve error feedback
Diffstat (limited to 'frontend')
-rw-r--r--frontend/index.html7
-rw-r--r--frontend/js/app.js78
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
21function getProfile(accessToken, callback) { 21function 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,