فهرست منبع

Make the accounts sortable and persist new order.
Deactivate Pull-to-refresh feature to prevent side effects

Bubka 5 سال پیش
والد
کامیت
eaabe6e9e3

+ 13 - 0
app/Http/Controllers/TwoFAccountController.php

@@ -62,6 +62,19 @@ class TwoFAccountController extends Controller
     }
 
 
+    /**
+     * Save new order.
+     *
+     * @param  \App\TwoFAccount  $twofaccount
+     * @return \Illuminate\Http\Response
+     */
+    public function reorder(Request $request)
+    {
+        TwoFAccount::setNewOrder($request->orderedIds);
+        return response()->json('order saved', 200);
+    }
+
+
     /**
      * Generate a TOTP
      *

+ 4 - 2
resources/js/packages/fontawesome.js

@@ -14,7 +14,8 @@ import {
     faLock,
     faLockOpen,
     faSearch,
-    faEllipsisH
+    faEllipsisH,
+    faBars
 } from '@fortawesome/free-solid-svg-icons'
 
 library.add(
@@ -27,7 +28,8 @@ library.add(
     faLock,
     faLockOpen,
     faSearch,
-    faEllipsisH
+    faEllipsisH,
+    faBars
 );
 
 Vue.component('font-awesome-icon', FontAwesomeIcon)

+ 51 - 33
resources/js/views/Accounts.vue

@@ -26,39 +26,46 @@
                 </div>
             </div>
             <!-- accounts -->
-            <vue-pull-refresh :on-refresh="onRefresh" :config="{
+            <!-- <vue-pull-refresh :on-refresh="onRefresh" :config="{
                 errorLabel: 'error',
                 startLabel: '',
                 readyLabel: '',
                 loadingLabel: 'refreshing'
-                }" class="accounts columns is-multiline is-centered">
-                <div class="tfa column is-narrow has-text-white" v-for="account in filteredAccounts">
-                    <div class="tfa-container">
-	                    <transition name="slideCheckbox">
-	                        <div class="tfa-checkbox" v-if="editMode">
-	                            <div class="field">
-	                                <input class="is-checkradio is-small is-white" :id="'ckb_' + account.id" :value="account.id" type="checkbox" :name="'ckb_' + account.id" v-model="selectedAccounts">
-	                                <label :for="'ckb_' + account.id"></label>
-	                            </div>
-	                        </div>
-	                    </transition>
-                        <div class="tfa-content is-size-3 is-size-4-mobile" @click.stop="showAccount(account)">  
-                            <div class="tfa-text has-ellipsis">
-                                <img :src="'/storage/icons/' + account.icon" v-if="account.icon">
-                                {{ account.service }}
-                                <span class="is-family-primary is-size-6 is-size-7-mobile has-text-grey ">{{ account.account }}</span>
+                }" > -->
+                <draggable v-model="filteredAccounts" @end="saveOrder" handle=".tfa-dots" class="accounts columns is-multiline is-centered">
+                    <div class="tfa column is-narrow has-text-white" v-for="account in filteredAccounts" :key="account.id">
+                        <div class="tfa-container">
+    	                    <transition name="slideCheckbox">
+    	                        <div class="tfa-checkbox" v-if="editMode">
+    	                            <div class="field">
+    	                                <input class="is-checkradio is-small is-white" :id="'ckb_' + account.id" :value="account.id" type="checkbox" :name="'ckb_' + account.id" v-model="selectedAccounts">
+    	                                <label :for="'ckb_' + account.id"></label>
+    	                            </div>
+    	                        </div>
+    	                    </transition>
+                            <div class="tfa-content is-size-3 is-size-4-mobile" @click.stop="showAccount(account)">  
+                                <div class="tfa-text has-ellipsis">
+                                    <img :src="'/storage/icons/' + account.icon" v-if="account.icon">
+                                    {{ account.service }}
+                                    <span class="is-family-primary is-size-6 is-size-7-mobile has-text-grey ">{{ account.account }}</span>
+                                </div>
                             </div>
+    	                    <transition name="fadeInOut">
+    	                        <div class="tfa-edit has-text-grey" v-if="editMode">
+    	                            <router-link :to="{ name: 'edit', params: { twofaccountId: account.id }}" class="tag is-dark is-rounded">
+    	                                {{ $t('commons.edit') }}
+    	                            </router-link>
+    	                        </div>
+    	                    </transition>
+                            <transition name="fadeInOut">
+                                <div class="tfa-dots has-text-grey" v-if="editMode">
+                                    <font-awesome-icon :icon="['fas', 'bars']" />
+                                </div>
+                            </transition>
                         </div>
-	                    <transition name="fadeInOut">
-	                        <div class="tfa-dots has-text-grey" v-if="editMode">
-	                            <router-link :to="{ name: 'edit', params: { twofaccountId: account.id }}" class="tag is-dark is-rounded">
-	                                {{ $t('commons.edit') }}
-	                            </router-link>
-	                        </div>
-	                    </transition>
                     </div>
-                </div>
-            </vue-pull-refresh>
+                </draggable>
+            <!-- </vue-pull-refresh> -->
         </div>
         <!-- No account -->
         <div class="container has-text-centered" v-show="showQuickForm">
@@ -132,6 +139,7 @@
     import TwofaccountShow from '../components/TwofaccountShow'
     import Form from './../components/Form'
     import vuePullRefresh from 'vue-pull-refresh';
+    import draggable from 'vuedraggable'
 
     export default {
         data(){
@@ -149,12 +157,17 @@
         },
 
         computed: {
-            filteredAccounts() {
-                return this.accounts.filter(
-                    item => {
-                        return item.service.toLowerCase().includes(this.search.toLowerCase()) || item.account.toLowerCase().includes(this.search.toLowerCase());
-                    }
-                );
+            filteredAccounts: {
+                get: function() {
+                    return this.accounts.filter(
+                        item => {
+                            return item.service.toLowerCase().includes(this.search.toLowerCase()) || item.account.toLowerCase().includes(this.search.toLowerCase());
+                        }
+                    );
+                },
+                set: function(reorderedAccounts) {
+                    this.accounts = reorderedAccounts
+                }
             },
 
             showAccounts() {
@@ -179,7 +192,8 @@
         components: {
             Modal,
             TwofaccountShow,
-            'vue-pull-refresh': vuePullRefresh
+            'vue-pull-refresh': vuePullRefresh,
+            draggable,
         },
 
         methods: {
@@ -241,6 +255,10 @@
                 }
             },
 
+            saveOrder() {
+                this.axios.patch('/api/twofaccounts/reorder', {orderedIds: this.accounts.map(a => a.id)})
+            },
+
             deleteAccount:  function (id) {
                 if(confirm(this.$t('twofaccounts.confirm.delete'))) {
                     this.axios.delete('/api/twofaccounts/' + id)

+ 14 - 6
resources/sass/app.scss

@@ -52,7 +52,6 @@ a:hover {
 .tfa {
     border-radius: 6px;
     text-align: center;
-    cursor: pointer;
     background-color: hsl(0, 0%, 10%); /*black-bis from Bulma*/
     padding: 0.75rem 1.5rem;
     margin: 0.5rem;
@@ -89,22 +88,26 @@ a:hover {
   }
 
   .tfa-container > div:last-of-type {
-      padding: 0 0.5rem 0 0;
+      padding: 0 1rem 0 0;
   }
 }
 
-.tfa-checkbox, .tfa-dots {
+.tfa-checkbox, .tfa-dots, .tfa-edit {
   display: flex;
   align-items: center;
   padding: 0.5rem 0 0 0;
 }
 
 @media screen and (max-width: 768px) {
-  .tfa-checkbox, .tfa-dots {
+  .tfa-checkbox, .tfa-dots, .tfa-edit {
     display: flex;
     align-items: center;
       padding: 0
   }
+
+  .tfa-dots {
+    margin-left: 1.5rem;
+  }
 }
 
 .tfa-content {
@@ -118,7 +121,7 @@ a:hover {
 }
 
 .tfa-dots {
-  // order: 3;
+  cursor: grab;
 }
 
 @media screen and (max-width: 768px) {
@@ -130,9 +133,13 @@ a:hover {
       order: 2;
   }
 
-  .tfa-dots {
+  .tfa-edit {
     order: 3;
   }
+
+  .tfa-dots {
+    order: 4;
+  }
 }
 
 @media screen and (min-width: 769px) {
@@ -146,6 +153,7 @@ a:hover {
 .tfa-text {
     display: block;
     max-width: 300px;
+    cursor: pointer;
 }
 
 .tfa img {

+ 1 - 0
routes/api.php

@@ -37,6 +37,7 @@ Route::group(['middleware' => 'auth:api'], function() {
     });
 
     Route::delete('twofaccounts/batch', 'TwoFAccountController@batchDestroy');
+    Route::patch('twofaccounts/reorder', 'TwoFAccountController@reorder');
     Route::apiResource('twofaccounts', 'TwoFAccountController');
     Route::post('twofaccounts/otp', 'TwoFAccountController@generateOTP')->name('twofaccounts.generateOTP');
     Route::post('qrcode/decode', 'QrCodeController@decode');