From a058941aedcfe6a3e14e245b7270477a7a6e69be Mon Sep 17 00:00:00 2001
From: eal <eal@waifu.club>
Date: Sat, 23 Dec 2017 16:44:22 +0200
Subject: [PATCH] Add follow import to user settings.

---
 src/components/user_settings/user_settings.js | 31 ++++++++++++++++++-
 .../user_settings/user_settings.vue           | 17 ++++++++++
 src/i18n/messages.js                          |  6 +++-
 src/services/api/api.service.js               | 13 +++++++-
 .../backend_interactor_service.js             |  4 ++-
 5 files changed, 67 insertions(+), 4 deletions(-)

diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index fd20a6ad..25ee1f35 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -5,7 +5,10 @@ const UserSettings = {
     return {
       newname: this.$store.state.users.currentUser.name,
       newbio: this.$store.state.users.currentUser.description,
-      uploading: [ false, false, false ],
+      followList: null,
+      followImportError: false,
+      followsImported: false,
+      uploading: [ false, false, false, false ],
       previews: [ null, null, null ]
     }
   },
@@ -15,6 +18,9 @@ const UserSettings = {
   computed: {
     user () {
       return this.$store.state.users.currentUser
+    },
+    pleromaBackend () {
+      return this.$store.state.config.pleromaBackend
     }
   },
   methods: {
@@ -117,6 +123,29 @@ const UserSettings = {
         }
         this.uploading[2] = false
       })
+    },
+    importFollows () {
+      this.uploading[3] = true
+      const followList = this.followList
+      this.$store.state.api.backendInteractor.followImport({params: followList})
+        .then((status) => {
+          if (status) {
+            this.followsImported = true
+          } else {
+            this.followImportError = true
+          }
+          this.uploading[3] = false
+        })
+    },
+    followListChange () {
+      // eslint-disable-next-line no-undef
+      let formData = new FormData()
+      formData.append('list', this.$refs.followlist.files[0])
+      this.followList = formData
+    },
+    dismissImported () {
+      this.followsImported = false
+      this.followImportError = false
     }
   }
 }
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index 515fd253..da78cdc2 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -49,6 +49,23 @@
         <i class="base09 icon-spin4 animate-spin uploading" v-if="uploading[2]"></i>
         <button class="btn btn-default base05 base02-background" v-else-if="previews[2]" @click="submitBg">{{$t('general.submit')}}</button>
       </div>
+      <div class="setting-item" v-if="pleromaBackend">
+        <h3>{{$t('settings.follow_import')}}</h3>
+        <p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
+        <form v-model="followImportForm">
+          <input type="file" ref="followlist" v-on:change="followListChange"></input>
+        </form>
+        <i class="base09 icon-spin4 animate-spin uploading" v-if="uploading[3]"></i>
+        <button class="btn btn-default base05 base02-background" 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>
+      </div>
     </div>
   </div>
 </template>
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
index 4c5be151..360f9eb8 100644
--- a/src/i18n/messages.js
+++ b/src/i18n/messages.js
@@ -242,7 +242,11 @@ const en = {
     nsfw_clickthrough: 'Enable clickthrough NSFW attachment hiding',
     autoload: 'Enable automatic loading when scrolled to the bottom',
     streaming: 'Enable automatic streaming of new posts when scrolled to the top',
-    reply_link_preview: 'Enable reply-link preview on mouse hover'
+    reply_link_preview: 'Enable reply-link preview on mouse hover',
+    follow_import: 'Follow import',
+    import_followers_from_a_csv_file: 'Import followers from a csv file',
+    follows_imported: 'Follows imported! Processing them will take a while.',
+    follow_import_error: 'Error importing followers'
   },
   notifications: {
     notifications: 'Notifications',
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 5b078bc8..1f5b3ad2 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -29,6 +29,7 @@ const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json'
 const BLOCKING_URL = '/api/blocks/create.json'
 const UNBLOCKING_URL = '/api/blocks/destroy.json'
 const USER_URL = '/api/users/show.json'
+const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
 
 import { each, map } from 'lodash'
 import 'whatwg-fetch'
@@ -362,6 +363,15 @@ const uploadMedia = ({formData, credentials}) => {
     .then((text) => (new DOMParser()).parseFromString(text, 'application/xml'))
 }
 
+const followImport = ({params, credentials}) => {
+  return fetch(FOLLOW_IMPORT_URL, {
+    body: params,
+    method: 'POST',
+    headers: authHeaders(credentials)
+  })
+    .then((response) => response.ok)
+}
+
 const fetchMutes = ({credentials}) => {
   const url = '/api/qvitter/mutes.json'
 
@@ -396,7 +406,8 @@ const apiService = {
   updateBg,
   updateProfile,
   updateBanner,
-  externalProfile
+  externalProfile,
+  followImport
 }
 
 export default apiService
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index ddaae3b2..52b8286b 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -59,6 +59,7 @@ const backendInteractorService = (credentials) => {
   const updateProfile = ({params}) => apiService.updateProfile({credentials, params})
 
   const externalProfile = (profileUrl) => apiService.externalProfile({profileUrl, credentials})
+  const followImport = ({params}) => apiService.followImport({params, credentials})
 
   const backendInteractorServiceInstance = {
     fetchStatus,
@@ -80,7 +81,8 @@ const backendInteractorService = (credentials) => {
     updateBg,
     updateBanner,
     updateProfile,
-    externalProfile
+    externalProfile,
+    followImport
   }
 
   return backendInteractorServiceInstance