From da6d64f9ee1b00dd9cbefb8d684254424bc324c7 Mon Sep 17 00:00:00 2001
From: Bubka <858858+Bubka@users.noreply.github.com>
Date: Wed, 8 Jan 2020 23:22:51 +0100
Subject: [PATCH] Refactore and fix for icon management

---
 app/Http/Controllers/IconController.php       |  8 +--
 .../Controllers/TwoFAccountController.php     |  2 +-
 app/TwoFAccount.php                           |  2 +-
 resources/js/components/TwofaccountShow.vue   |  2 +-
 resources/js/views/Accounts.vue               |  4 +-
 resources/js/views/Create.vue                 | 57 +++++++++------
 resources/js/views/Edit.vue                   | 70 +++++++++++++------
 7 files changed, 94 insertions(+), 51 deletions(-)

diff --git a/app/Http/Controllers/IconController.php b/app/Http/Controllers/IconController.php
index 9e726218..bde6a8aa 100644
--- a/app/Http/Controllers/IconController.php
+++ b/app/Http/Controllers/IconController.php
@@ -21,9 +21,9 @@ public function upload(Request $request)
 
         if($request->hasFile('icon')){
 
-            $path = $request->file('icon')->storePublicly('public');
+            $path = $request->file('icon')->storePublicly('public/icons');
 
-            return response()->json('storage/' . pathinfo($path)['basename'], 201);
+            return response()->json(pathinfo($path)['basename'], 201);
         }
         else
         {
@@ -41,9 +41,9 @@ public function upload(Request $request)
     public function delete($icon)
     {
 
-        if( Storage::exists('public/' . $icon) ) {
+        if( Storage::exists('public/icons/' . $icon) ) {
 
-            Storage::delete('public/' . $icon); 
+            Storage::delete('public/icons/' . $icon); 
         }
 
         return response()->json(null, 204);
diff --git a/app/Http/Controllers/TwoFAccountController.php b/app/Http/Controllers/TwoFAccountController.php
index 4c59a6cf..1e6890a4 100644
--- a/app/Http/Controllers/TwoFAccountController.php
+++ b/app/Http/Controllers/TwoFAccountController.php
@@ -108,7 +108,7 @@ public function update(Request $request, TwoFAccount $twofaccount)
     public function destroy(TwoFAccount $twofaccount)
     {
         // delete icon
-        $storedIcon = 'public/' . pathinfo($twofaccount->icon)['basename'];
+        $storedIcon = 'public/icons/' . $twofaccount->icon;
 
         if( Storage::exists($storedIcon) ) {
             Storage::delete($storedIcon);
diff --git a/app/TwoFAccount.php b/app/TwoFAccount.php
index fd95b110..bf1205ae 100644
--- a/app/TwoFAccount.php
+++ b/app/TwoFAccount.php
@@ -28,7 +28,7 @@ class TwoFAccount extends Model
     public function getIconAttribute($value)
     {
 
-        if( !Storage::exists('public/' . pathinfo($value)['basename']) ) {
+        if( !Storage::exists('public/icons/' . pathinfo($value)['basename']) ) {
 
             return '';
         }
diff --git a/resources/js/components/TwofaccountShow.vue b/resources/js/components/TwofaccountShow.vue
index 9445ded3..32d062dd 100644
--- a/resources/js/components/TwofaccountShow.vue
+++ b/resources/js/components/TwofaccountShow.vue
@@ -1,7 +1,7 @@
 <template>
     <div>
         <figure class="image is-64x64" style="display: inline-block"  v-if="icon">
-            <img :src="icon">
+            <img :src="'storage/icons/' + icon">
         </figure>
         <p class="is-size-4 has-text-grey-light">{{ service }}</p>
         <p class="is-size-6 has-text-grey">{{ account }}</p>
diff --git a/resources/js/views/Accounts.vue b/resources/js/views/Accounts.vue
index fe86d9c2..d089f2e3 100644
--- a/resources/js/views/Accounts.vue
+++ b/resources/js/views/Accounts.vue
@@ -4,7 +4,7 @@
             <div class="buttons are-large is-centered">
                 <span v-for="account in accounts" class="button is-black twofaccount" >
                     <span @click.stop="getAccount(account.id)">
-                        <img :src="account.icon" v-if="account.icon">
+                        <img :src="'storage/icons/' + account.icon" v-if="account.icon">
                         {{ account.service }}
                         <span class="is-family-primary is-size-7 has-text-grey">{{ account.account }}</span>
                     </span>
@@ -21,7 +21,7 @@
         </div>
         <div class="container has-text-centered" v-show="this.showNoAccount">
             <p>
-                <img class="bg" src="storage/bg.png">
+                <img class="bg" src="storage/img/bg.png">
             </p>
             <p class="subtitle is-5">
                 No 2FA here!
diff --git a/resources/js/views/Create.vue b/resources/js/views/Create.vue
index a80c44a2..bdb6e6ac 100644
--- a/resources/js/views/Create.vue
+++ b/resources/js/views/Create.vue
@@ -63,8 +63,8 @@
                                     <span class="file-label">Choose an image…</span>
                                 </span>
                             </label>
-                            <span class="tag is-black is-large" v-if="twofaccount.icon.length > 0">
-                                <img class="icon-preview" :src="twofaccount.icon" >
+                            <span class="tag is-black is-large" v-if="tempIcon">
+                                <img class="icon-preview" :src="'storage/icons/' + tempIcon" >
                                 <button class="delete is-small" @click.prevent="deleteIcon"></button>
                             </span>
                         </div>
@@ -74,7 +74,7 @@
                             <button type="submit" class="button is-link">Create</button>
                         </div>
                         <div class="control">
-                            <router-link :to="{ name: 'accounts', params: { InitialEditMode: false } }" class="button is-text">Cancel</router-link>
+                            <button class="button is-text" @click="cancelCreation">Cancel</button>
                         </div>
                     </div>
                 </form>
@@ -93,13 +93,18 @@
                     'uri' : '',
                     'icon' : ''
                 },
-                uriIsLocked: true
+                uriIsLocked: true,
+                tempIcon: ''
             }
         },
 
         methods: {
 
             createAccount: function() {
+                // set current temp icon as account icon
+                this.twofaccount.icon = this.tempIcon
+
+                // store the account
                 let token = localStorage.getItem('jwt')
 
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
@@ -110,6 +115,15 @@
                 })
             },
 
+            cancelCreation: function() {
+                // clean possible uploaded temp icon
+                if( this.tempIcon ) {
+                    this.deleteIcon()
+                }
+
+                this.$router.push({name: 'accounts', params: { InitialEditMode: false }});
+            },
+
             uploadQrcode(event) {
 
                 let token = localStorage.getItem('jwt')
@@ -117,15 +131,15 @@
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
                 axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
 
-                 let files = this.$refs.qrcodeInput.files
+                let files = this.$refs.qrcodeInput.files
 
-                 if (!files.length) {
-                     console.log('no files');
-                     return false;
-                 }
-                 else {
+                if (!files.length) {
+                    console.log('no files');
+                    return false;
+                }
+                else {
                     console.log(files.length + ' file(s) found');
-                 }
+                }
 
                 let imgdata = new FormData();
 
@@ -151,16 +165,16 @@
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
                 axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
 
-                 let files = this.$refs.iconInput.files
+                let files = this.$refs.iconInput.files
 
-                 if (!files.length) {
-                     return false;
-                 }
+                if (!files.length) {
+                    return false;
+                }
 
-                 // clean possible already uploaded icon
-                 if( this.twofaccount.icon ) {
+                // clean possible already uploaded temp icon
+                if( this.tempIcon ) {
                     this.deleteIcon()
-                 }
+                }
 
                 let imgdata = new FormData();
 
@@ -174,7 +188,7 @@
 
                 axios.post('/api/icon/upload', imgdata, config).then(response => {
                         console.log('icon path > ', response);
-                        this.twofaccount.icon = response.data;
+                        this.tempIcon = response.data;
                     }
                 )
             },
@@ -186,11 +200,10 @@
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
                 axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
 
-                axios.delete('/api/icon/delete/' + this.twofaccount.icon.replace('storage/', '')).then(response => {
-                        this.twofaccount.icon = ''
+                axios.delete('/api/icon/delete/' + this.tempIcon).then(response => {
+                        this.tempIcon = ''
                     }
                 )
-                
             }
             
         },
diff --git a/resources/js/views/Edit.vue b/resources/js/views/Edit.vue
index df825e36..fb23ca50 100644
--- a/resources/js/views/Edit.vue
+++ b/resources/js/views/Edit.vue
@@ -28,8 +28,8 @@
                                     <span class="file-label">Choose an image…</span>
                                 </span>
                             </label>
-                            <span class="tag is-black is-large" v-if="twofaccount.icon">
-                                <img class="icon-preview" :src="'../' + twofaccount.icon" >
+                            <span class="tag is-black is-large" v-if="tempIcon">
+                                <img class="icon-preview" :src="'../storage/icons/' + tempIcon" >
                                 <button class="delete is-small" @click.prevent="deleteIcon"></button>
                             </span>
                         </div>
@@ -39,7 +39,7 @@
                             <button type="submit" class="button is-link">Save</button>
                         </div>
                         <div class="control">
-                            <router-link :to="{ name: 'accounts', params: { InitialEditMode: true } }" class="button is-text">Cancel</router-link>
+                            <button class="button is-text" @click.prevent="cancelCreation">Cancel</button>
                         </div>
                     </div>
                 </form>
@@ -57,7 +57,8 @@
                     'account' : '',
                     'uri' : '',
                     'icon' : ''
-                }
+                },
+                tempIcon: ''
             }
         },
 
@@ -74,10 +75,26 @@
 
                 axios.get('/api/twofaccounts/' + this.$route.params.twofaccountId).then(response => {
                     this.twofaccount = response.data
+
+                    // set account icon as temp icon
+                    this.tempIcon = this.twofaccount.icon
                 })
             },
 
             updateAccount: function() {
+
+                // Set new icon and delete old one
+                if( this.tempIcon !== this.twofaccount.icon ) {
+                    let oldIcon = ''
+
+                    oldIcon = this.twofaccount.icon
+
+                    this.twofaccount.icon = this.tempIcon
+                    this.tempIcon = oldIcon
+                    this.deleteIcon()
+                }
+
+                // store the account
                 let token = localStorage.getItem('jwt')
 
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
@@ -88,6 +105,15 @@
                 })
             },
 
+            cancelCreation: function() {
+                // clean new temp icon
+                if( this.tempIcon ) {
+                    this.deleteIcon()
+                }
+
+                this.$router.push({name: 'accounts', params: { InitialEditMode: true }});
+            },
+
             uploadIcon(event) {
 
                 let token = localStorage.getItem('jwt')
@@ -95,16 +121,16 @@
                 axios.defaults.headers.common['Content-Type'] = 'application/json'
                 axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
 
-                 let files = this.$refs.iconInput.files
+                let files = this.$refs.iconInput.files
 
-                 if (!files.length) {
-                     return false;
-                 }
+                if (!files.length) {
+                    return false;
+                }
 
-                 // clean possible already uploaded icon
-                 if( this.twofaccount.icon ) {
+                // clean possible tempIcon but keep original one
+                if( this.tempIcon && this.tempIcon !== this.twofaccount.icon ) {
                     this.deleteIcon()
-                 }
+                }
 
                 let imgdata = new FormData();
 
@@ -118,23 +144,27 @@
 
                 axios.post('/api/icon/upload', imgdata, config).then(response => {
                         console.log('icon path > ', response);
-                        this.twofaccount.icon = response.data;
+                        this.tempIcon = response.data;
                     }
                 )
             },
 
             deleteIcon(event) {
 
-                let token = localStorage.getItem('jwt')
+                if( this.tempIcon !== this.twofaccount.icon ) {
+                    let token = localStorage.getItem('jwt')
 
-                axios.defaults.headers.common['Content-Type'] = 'application/json'
-                axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
+                    axios.defaults.headers.common['Content-Type'] = 'application/json'
+                    axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
 
-                axios.delete('/api/icon/delete/' + this.twofaccount.icon.replace('storage/', '')).then(response => {
-                        this.twofaccount.icon = ''
-                    }
-                )
-            }
+                    axios.delete('/api/icon/delete/' + this.tempIcon).then(response => {
+                            this.tempIcon = ''
+                        }
+                    )
+                }
+
+                this.tempIcon = ''
+            },
 
         },