From 562120ae48945d87a24f2248f9b16185b49159d0 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Fri, 29 Mar 2019 21:58:20 -0400
Subject: split out follow’s importer as a separate component
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/user_settings/user_settings.vue | 14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index d68e68fa..fb91e269 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -171,19 +171,7 @@
           <div class="setting-item">
             <h2>{{$t('settings.follow_import')}}</h2>
             <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
-            <form>
-              <input type="file" ref="followlist" v-on:change="followListChange" />
-            </form>
-            <i class=" icon-spin4 animate-spin uploading" v-if="followListUploading"></i>
-            <button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
-            <div v-if="followsImported">
-              <i class="icon-cross" @click="dismissImported"></i>
-              <p>{{$t('settings.follows_imported')}}</p>
-            </div>
-            <div v-else-if="followImportError">
-              <i class="icon-cross" @click="dismissImported"></i>
-              <p>{{$t('settings.follow_import_error')}}</p>
-            </div>
+            <Importer />
           </div>
           <div class="setting-item" v-if="enableFollowsExport">
             <h2>{{$t('settings.follow_export')}}</h2>
-- 
cgit v1.2.3-70-g09d2


From 6d0e98a1c2c81c587b89736dbd2ac43a8c540d54 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Sat, 30 Mar 2019 05:10:57 -0400
Subject: make Importer component reusable

---
 src/components/importer/importer.js            | 42 +++++++++++++++++++-------
 src/components/importer/importer.vue           | 10 +++---
 src/components/user_settings/user_settings.js  |  8 +++++
 src/components/user_settings/user_settings.vue |  2 +-
 src/i18n/en.json                               |  5 +++
 5 files changed, 50 insertions(+), 17 deletions(-)

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/importer/importer.js b/src/components/importer/importer.js
index 44d02c93..c5f9e4d2 100644
--- a/src/components/importer/importer.js
+++ b/src/components/importer/importer.js
@@ -1,10 +1,34 @@
 const Importer = {
+  props: {
+    submitHandler: {
+      type: Function,
+      required: true
+    },
+    submitButtonLabel: {
+      type: String,
+      default () {
+        return this.$t('importer.submit')
+      }
+    },
+    successMessage: {
+      type: String,
+      default () {
+        return this.$t('importer.success')
+      }
+    },
+    errorMessage: {
+      type: String,
+      default () {
+        return this.$t('importer.error')
+      }
+    }
+  },
   data () {
     return {
       file: null,
       error: false,
       success: false,
-      uploading: false
+      submitting: false
     }
   },
   methods: {
@@ -12,16 +36,12 @@ const Importer = {
       this.file = this.$refs.input.files[0]
     },
     submit () {
-      this.uploading = true
-      this.$store.state.api.backendInteractor.followImport(this.file)
-        .then((status) => {
-          if (status) {
-            this.success = true
-          } else {
-            this.error = true
-          }
-          this.uploading = false
-        })
+      this.dismiss()
+      this.submitting = true
+      this.submitHandler(this.file)
+        .then(() => { this.success = true })
+        .catch(() => { this.error = true })
+        .finally(() => { this.submitting = false })
     },
     dismiss () {
       this.success = false
diff --git a/src/components/importer/importer.vue b/src/components/importer/importer.vue
index d2447e1a..0c5aa93d 100644
--- a/src/components/importer/importer.vue
+++ b/src/components/importer/importer.vue
@@ -3,15 +3,15 @@
     <form>
       <input type="file" ref="input" v-on:change="change" />
     </form>
-    <i class="icon-spin4 animate-spin uploading" v-if="uploading"></i>
-    <button class="btn btn-default" v-else @click="submit">{{$t('general.submit')}}</button>
+    <i class="icon-spin4 animate-spin importer-uploading" v-if="submitting"></i>
+    <button class="btn btn-default" v-else @click="submit">{{submitButtonLabel}}</button>
     <div v-if="success">
       <i class="icon-cross" @click="dismiss"></i>
-      <p>{{$t('settings.follows_imported')}}</p>
+      <p>{{successMessage}}</p>
     </div>
     <div v-else-if="error">
       <i class="icon-cross" @click="dismiss"></i>
-      <p>{{$t('settings.follow_import_error')}}</p>
+      <p>{{errorMessage}}</p>
     </div>
   </div>
 </template>
@@ -20,7 +20,7 @@
 
 <style lang="scss">
 .importer {
-  .uploading {
+  &-uploading {
     font-size: 1.5em;
     margin: 0.25em;
   }
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index a213650b..32e92dc4 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -234,6 +234,14 @@ const UserSettings = {
         this.backgroundUploading = false
       })
     },
+    importFollows (file) {
+      return this.$store.state.api.backendInteractor.followImport(file)
+        .then((status) => {
+          if (!status) {
+            throw new Error('failed')
+          }
+        })
+    },
     /* This function takes an Array of Users
      * and outputs a file with all the addresses for the user to download
      */
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index fb91e269..fc40bdc0 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -171,7 +171,7 @@
           <div class="setting-item">
             <h2>{{$t('settings.follow_import')}}</h2>
             <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
-            <Importer />
+            <Importer :submitHandler="importFollows" :successMessage="$t('settings.follows_imported')" :errorMessage="$t('settings.follow_import_error')" />
           </div>
           <div class="setting-item" v-if="enableFollowsExport">
             <h2>{{$t('settings.follow_export')}}</h2>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 711e8d31..34d252b2 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -31,6 +31,11 @@
     "save_without_cropping": "Save without cropping",
     "cancel": "Cancel"
   },
+  "importer": {
+    "submit": "Submit",
+    "success": "Imported successfully.",
+    "error": "An error occured while importing this file."
+  },
   "login": {
     "login": "Log in",
     "description": "Log in with OAuth",
-- 
cgit v1.2.3-70-g09d2


From 0ab2f9dfa58c1f4caf01f083175a171d19272cda Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Sat, 30 Mar 2019 07:27:53 -0400
Subject: add “block import” feature
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/user_settings/user_settings.js               |  8 ++++++++
 src/components/user_settings/user_settings.vue              |  5 +++++
 src/i18n/en.json                                            |  4 ++++
 src/services/api/api.service.js                             | 13 +++++++++++++
 .../backend_interactor_service.js                           |  2 ++
 5 files changed, 32 insertions(+)

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index fa252e59..c4214744 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -242,6 +242,14 @@ const UserSettings = {
           }
         })
     },
+    importBlocks (file) {
+      return this.$store.state.api.backendInteractor.importBlocks(file)
+        .then((status) => {
+          if (!status) {
+            throw new Error('failed')
+          }
+        })
+    },
     /* This function takes an Array of Users
      * and outputs a file with all the addresses for the user to download
      */
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index fc40bdc0..520a3d8a 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -180,6 +180,11 @@
           <div class="setting-item" v-else>
             <h2>{{$t('settings.follow_export_processing')}}</h2>
           </div>
+          <div class="setting-item">
+            <h2>{{$t('settings.block_import')}}</h2>
+            <p>{{$t('settings.import_blocks_from_a_csv_file')}}</p>
+            <Importer :submitHandler="importFollows" :successMessage="$t('settings.blocks_imported')" :errorMessage="$t('settings.block_import_error')" />
+          </div>
         </div>
 
         <div :label="$t('settings.blocks_tab')">
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 34d252b2..d4ec1134 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -131,6 +131,9 @@
     "avatarRadius": "Avatars",
     "background": "Background",
     "bio": "Bio",
+    "block_import": "Block import",
+    "block_import_error": "Error importing blocks",
+    "blocks_imported": "Blocks imported! Processing them will take a while.",
     "blocks_tab": "Blocks",
     "btnRadius": "Buttons",
     "cBlue": "Blue (Reply, follow)",
@@ -174,6 +177,7 @@
     "hide_post_stats": "Hide post statistics (e.g. the number of favorites)",
     "hide_user_stats": "Hide user statistics (e.g. the number of followers)",
     "hide_filtered_statuses": "Hide filtered statuses",
+    "import_blocks_from_a_csv_file": "Import blocks from a csv file",
     "import_followers_from_a_csv_file": "Import follows from a csv file",
     "import_theme": "Load preset",
     "inputRadius": "Input fields",
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index dbcde41d..a6892959 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -9,6 +9,7 @@ const BANNER_UPDATE_URL = '/api/account/update_profile_banner.json'
 const PROFILE_UPDATE_URL = '/api/account/update_profile.json'
 const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json'
 const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json'
+const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
 const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
 const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
 const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
@@ -634,6 +635,17 @@ const uploadMedia = ({formData, credentials}) => {
     .then((data) => parseAttachment(data))
 }
 
+const importBlocks = ({file, credentials}) => {
+  const formData = new FormData()
+  formData.append('list', file)
+  return fetch(BLOCKS_IMPORT_URL, {
+    body: formData,
+    method: 'POST',
+    headers: authHeaders(credentials)
+  })
+    .then((response) => response.ok)
+}
+
 const importFollows = ({file, credentials}) => {
   const formData = new FormData()
   formData.append('list', file)
@@ -778,6 +790,7 @@ const apiService = {
   updateProfile,
   updateBanner,
   externalProfile,
+  importBlocks,
   importFollows,
   deleteAccount,
   changePassword,
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index 726c8ced..3256a921 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -107,6 +107,7 @@ const backendInteractorService = (credentials) => {
   const updateProfile = ({params}) => apiService.updateProfile({credentials, params})
 
   const externalProfile = (profileUrl) => apiService.externalProfile({profileUrl, credentials})
+  const importBlocks = (file) => apiService.importBlocks({file, credentials})
   const importFollows = (file) => apiService.importFollows({file, credentials})
 
   const deleteAccount = ({password}) => apiService.deleteAccount({credentials, password})
@@ -147,6 +148,7 @@ const backendInteractorService = (credentials) => {
     updateBanner,
     updateProfile,
     externalProfile,
+    importBlocks,
     importFollows,
     deleteAccount,
     changePassword,
-- 
cgit v1.2.3-70-g09d2


From 08eaf9bd33fff53909e3f240e26f246c5fb96892 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Sat, 30 Mar 2019 08:03:40 -0400
Subject: make reusable Exporter component

---
 src/components/exporter/exporter.js            | 47 +++++++++++++++++++++++++
 src/components/exporter/exporter.vue           | 20 +++++++++++
 src/components/user_settings/user_settings.js  | 48 ++++++++------------------
 src/components/user_settings/user_settings.vue |  7 ++--
 src/i18n/en.json                               |  5 ++-
 5 files changed, 88 insertions(+), 39 deletions(-)
 create mode 100644 src/components/exporter/exporter.js
 create mode 100644 src/components/exporter/exporter.vue

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/exporter/exporter.js b/src/components/exporter/exporter.js
new file mode 100644
index 00000000..2ae9492c
--- /dev/null
+++ b/src/components/exporter/exporter.js
@@ -0,0 +1,47 @@
+const Exporter = {
+  props: {
+    getContent: {
+      type: Function,
+      required: true
+    },
+    filename: {
+      type: String,
+      default: 'export.csv'
+    },
+    exportButtonLabel: {
+      type: String,
+      default () {
+        return this.$t('exporter.export')
+      }
+    },
+    processingMessage: {
+      type: String,
+      default () {
+        return this.$t('exporter.processing')
+      }
+    }
+  },
+  data () {
+    return {
+      processing: false
+    }
+  },
+  methods: {
+    process () {
+      this.processing = true
+      this.getContent()
+        .then((content) => {
+          const fileToDownload = document.createElement('a')
+          fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content))
+          fileToDownload.setAttribute('download', this.filename)
+          fileToDownload.style.display = 'none'
+          document.body.appendChild(fileToDownload)
+          fileToDownload.click()
+          document.body.removeChild(fileToDownload)
+          setTimeout(() => { this.processing = false }, 2000)
+        })
+    }
+  }
+}
+
+export default Exporter
diff --git a/src/components/exporter/exporter.vue b/src/components/exporter/exporter.vue
new file mode 100644
index 00000000..f22e579e
--- /dev/null
+++ b/src/components/exporter/exporter.vue
@@ -0,0 +1,20 @@
+<template>
+  <div class="exporter">
+    <div v-if="processing">
+      <i class="icon-spin4 animate-spin exporter-processing"></i>
+      <span>{{processingMessage}}</span>
+    </div>
+    <button class="btn btn-default" @click="process" v-else>{{exportButtonLabel}}</button>
+  </div>
+</template>
+
+<script src="./exporter.js"></script>
+
+<style lang="scss">
+.exporter {
+  &-processing {
+    font-size: 1.5em;
+    margin: 0.25em;
+  }
+}
+</style>
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index c4214744..d40301f2 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -14,6 +14,7 @@ import ProgressButton from '../progress_button/progress_button.vue'
 import EmojiInput from '../emoji-input/emoji-input.vue'
 import Autosuggest from '../autosuggest/autosuggest.vue'
 import Importer from '../importer/importer.vue'
+import Exporter from '../exporter/exporter.vue'
 import withSubscription from '../../hocs/with_subscription/with_subscription'
 import userSearchApi from '../../services/new_api/user_search.js'
 
@@ -41,7 +42,6 @@ const UserSettings = {
       hideFollowers: this.$store.state.users.currentUser.hide_followers,
       showRole: this.$store.state.users.currentUser.show_role,
       role: this.$store.state.users.currentUser.role,
-      enableFollowsExport: true,
       pickAvatarBtnVisible: true,
       bannerUploading: false,
       backgroundUploading: false,
@@ -73,7 +73,8 @@ const UserSettings = {
     BlockCard,
     MuteCard,
     ProgressButton,
-    Importer
+    Importer,
+    Exporter
   },
   computed: {
     user () {
@@ -250,38 +251,19 @@ const UserSettings = {
           }
         })
     },
-    /* This function takes an Array of Users
-     * and outputs a file with all the addresses for the user to download
-     */
-    exportPeople (users, filename) {
-      // Get all the friends addresses
-      var UserAddresses = users.map(function (user) {
-        // check is it's a local user
-        if (user && user.is_local) {
-          // append the instance address
-          // eslint-disable-next-line no-undef
-          user.screen_name += '@' + location.hostname
-        }
-        return user.screen_name
-      }).join('\n')
-      // Make the user download the file
-      var fileToDownload = document.createElement('a')
-      fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(UserAddresses))
-      fileToDownload.setAttribute('download', filename)
-      fileToDownload.style.display = 'none'
-      document.body.appendChild(fileToDownload)
-      fileToDownload.click()
-      document.body.removeChild(fileToDownload)
-    },
-    exportFollows () {
-      this.enableFollowsExport = false
-      this.$store.state.api.backendInteractor
-        .exportFriends({
-          id: this.$store.state.users.currentUser.id
-        })
+    getFollowsContent () {
+      return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })
         .then((friendList) => {
-          this.exportPeople(friendList, 'friends.csv')
-          setTimeout(() => { this.enableFollowsExport = true }, 2000)
+          // Get all the friends addresses
+          return friendList.map((user) => {
+            // check is it's a local user
+            if (user && user.is_local) {
+              // append the instance address
+              // eslint-disable-next-line no-undef
+              return user.screen_name + '@' + location.hostname
+            }
+            return user.screen_name
+          }).join('\n')
         })
     },
     confirmDelete () {
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index 520a3d8a..ef77aaba 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -173,12 +173,9 @@
             <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
             <Importer :submitHandler="importFollows" :successMessage="$t('settings.follows_imported')" :errorMessage="$t('settings.follow_import_error')" />
           </div>
-          <div class="setting-item" v-if="enableFollowsExport">
+          <div class="setting-item">
             <h2>{{$t('settings.follow_export')}}</h2>
-            <button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button>
-          </div>
-          <div class="setting-item" v-else>
-            <h2>{{$t('settings.follow_export_processing')}}</h2>
+            <Exporter :getContent="getFollowsContent" filename="friends.csv" :exportButtonLabel="$t('settings.follow_export_button')" />
           </div>
           <div class="setting-item">
             <h2>{{$t('settings.block_import')}}</h2>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index d4ec1134..10283024 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -2,6 +2,10 @@
   "chat": {
     "title": "Chat"
   },
+  "exporter": {
+    "export": "Export",
+    "processing": "Processing, you'll soon be asked to download your file"
+  },
   "features_panel": {
     "chat": "Chat",
     "gopher": "Gopher",
@@ -161,7 +165,6 @@
     "filtering_explanation": "All statuses containing these words will be muted, one per line",
     "follow_export": "Follow export",
     "follow_export_button": "Export your follows to a csv file",
-    "follow_export_processing": "Processing, you'll soon be asked to download your file",
     "follow_import": "Follow import",
     "follow_import_error": "Error importing followers",
     "follows_imported": "Follows imported! Processing them will take a while.",
-- 
cgit v1.2.3-70-g09d2


From 13abe64f875220eb5ff5424187bc771acf46190e Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Sat, 30 Mar 2019 08:07:49 -0400
Subject: fix wrong function binding

---
 src/components/user_settings/user_settings.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index ef77aaba..db8c1ea9 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -180,7 +180,7 @@
           <div class="setting-item">
             <h2>{{$t('settings.block_import')}}</h2>
             <p>{{$t('settings.import_blocks_from_a_csv_file')}}</p>
-            <Importer :submitHandler="importFollows" :successMessage="$t('settings.blocks_imported')" :errorMessage="$t('settings.block_import_error')" />
+            <Importer :submitHandler="importBlocks" :successMessage="$t('settings.blocks_imported')" :errorMessage="$t('settings.block_import_error')" />
           </div>
         </div>
 
-- 
cgit v1.2.3-70-g09d2


From 95bc2d727b8a94e9050a71c27838eb2a0d765011 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Sat, 30 Mar 2019 08:14:52 -0400
Subject: add “export blocks” feature
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/user_settings/user_settings.js  | 19 +++++++++++++++++--
 src/components/user_settings/user_settings.vue |  4 ++++
 src/i18n/en.json                               |  2 ++
 3 files changed, 23 insertions(+), 2 deletions(-)

(limited to 'src/components/user_settings/user_settings.vue')

diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index d40301f2..de8b4ebf 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -253,9 +253,24 @@ const UserSettings = {
     },
     getFollowsContent () {
       return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })
-        .then((friendList) => {
+        .then((users) => {
+          // Get all the friends addresses
+          return users.map((user) => {
+            // check is it's a local user
+            if (user && user.is_local) {
+              // append the instance address
+              // eslint-disable-next-line no-undef
+              return user.screen_name + '@' + location.hostname
+            }
+            return user.screen_name
+          }).join('\n')
+        })
+    },
+    getBlocksContent () {
+      return this.$store.state.api.backendInteractor.fetchBlocks()
+        .then((users) => {
           // Get all the friends addresses
-          return friendList.map((user) => {
+          return users.map((user) => {
             // check is it's a local user
             if (user && user.is_local) {
               // append the instance address
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index db8c1ea9..8a94f0b8 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -182,6 +182,10 @@
             <p>{{$t('settings.import_blocks_from_a_csv_file')}}</p>
             <Importer :submitHandler="importBlocks" :successMessage="$t('settings.blocks_imported')" :errorMessage="$t('settings.block_import_error')" />
           </div>
+          <div class="setting-item">
+            <h2>{{$t('settings.block_export')}}</h2>
+            <Exporter :getContent="getBlocksContent" filename="blocks.csv" :exportButtonLabel="$t('settings.block_export_button')" />
+          </div>
         </div>
 
         <div :label="$t('settings.blocks_tab')">
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 10283024..173e0de2 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -135,6 +135,8 @@
     "avatarRadius": "Avatars",
     "background": "Background",
     "bio": "Bio",
+    "block_export": "Block export",
+    "block_export_button": "Export your blocks to a csv file",
     "block_import": "Block import",
     "block_import_error": "Error importing blocks",
     "blocks_imported": "Blocks imported! Processing them will take a while.",
-- 
cgit v1.2.3-70-g09d2

