Skip to content
Snippets Groups Projects
Suggestions.vue 7.93 KiB
Newer Older
  • Learn to ignore specific revisions
  • <template>
      <div>
        <ul class="collection with-header">
          <li class="collection-header">
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            <router-link to="/connect" class="secondary-content">Add another account</router-link>
            <h5>Connected accounts</h5>
    
          <li class="collection-item avatar" v-for="account in accounts" :key="account.fullId">
    
            <img v-if="account._source.getAvatar(account)" :src="account._source.getAvatar(account)" alt="" class="circle">
            <span class="title">{{ account._source.getDisplayName(account) }}</span>
            <p>{{ account._source.label }}</p>
    
            <div v-if="isLoadingSources && results[account.id] && results[account.id].results.isLoading">
    
    Eliot Berriot's avatar
    Eliot Berriot committed
              <div class="progress">
                <div v-if="results[account.id].results.progress === 'indeterminate'" :class="indeterminate"></div>
                <div v-else class="determinate" :style="{width: `${results[account.id].results.progress}%`}"></div>
              </div>
              {{ results[account.id].results.status }}
            </div>
            <a @click="fetch(account.id)" class="secondary-content"><i class="material-icons">refresh</i></a>
    
        <h2>
          Suggestions
          <button
            @click="fetch()"
            :class="['waves-effect', 'waves-light', {disabled: isLoading}, 'btn-small']" :disabled="isLoading">
            <i class="material-icons left">refresh</i>Fetch data
          </button>
        </h2>
    
        <div v-if="isLoading" class="progress">
          <div class="indeterminate"></div>
        </div>
    
        <ul class="collection">
          <li class="collection-item avatar" v-for="suggestion in filteredSuggestions" :key="suggestion.fullId">
            <img v-if="suggestion.avatar" :src="suggestion.avatar" alt="" class="circle">
            <a target="_blank" rel="noopener noreferrer" :href="suggestion.url" class="title">{{ suggestion.name }}</a>
            <p>Score: {{ suggestion.weight }}</p>
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            <a v-if="retributeProfiles[suggestion.fullId] === undefined" @click="lookup(suggestion.fullId)" class="secondary-content"><i class="material-icons">search</i></a>
            <div v-else-if="retributeProfiles[suggestion.fullId]">
              <h6>Donation platforms</h6>
              <!-- {{ retributeProfiles[suggestion.fullId] }} -->
              <template v-for="mean in retributeProfiles[suggestion.fullId].means">
                <a
                  :href="mean.url"
    
                  :key="mean.provider + mean.id"
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                  target="_blank"
                  rel="noopener noreferrer"
                  :class="['waves-effect', 'waves-light', 'btn-small']">
                  <span :title="mean.summary">{{ mean.provider }}</span>
                </a>
    
              </template>
            </div>
            <div v-else>No retribute information found for this account</div>
    
          </li>
        </ul>
    
      </div>
    
    </template>
    
    <script>
    import sources from '@/sources'
    
    import config from '@/config'
    
    import orderBy from 'lodash/orderBy'
    
    import pull from 'lodash/pull'
    
    Eliot Berriot's avatar
    Eliot Berriot committed
    import chunk from 'lodash/chunk'
    import axios from 'axios'
    
    export default {
      data () {
        return {
    
          isLoadingSources: false,
          isLoadingRetribute: false,
    
          results: {},
    
    Eliot Berriot's avatar
    Eliot Berriot committed
          aggregatedSuggestions: this.$store.state.cache.aggregatedSuggestions || {},
          retributeProfiles: this.$store.state.cache.retributeProfiles || {},
    
          loadingRetributeProfiles: [],
    
          filters: {
            retributeOnly: true
          }
    
        }
      },
      computed: {
        accounts () {
          return this.$store.getters.sortedAccounts.map((a) => {
            a._source = sources.sources[a.source]
            return a
          })
    
        allSuggestions () {
          return Object.values(this.aggregatedSuggestions)
        },
    
        filteredSuggestions () {
    
          let self = this
          let suggestions = this.allSuggestions.filter((s) => {
            let f = true
    
            if (self.filters.retributeOnly != null) {
              f = !!this.retributeProfiles[s.fullId] === self.filters.retributeOnly
    
            }
            return f
          })
          return orderBy(suggestions, ['weight', 'id'], ['desc', 'asc'])
    
        missingRetributeProfiles () {
    
          return this.allSuggestions.filter((s) => {
    
            return this.retributeProfiles[s.fullId] === undefined
          })
    
        },
        isLoading () {
          return this.isLoadingSources || this.isLoadingRetribute
    
        clearCache () {
    
          this.isLoadingSources = false
          this.isLoadingRetribute = false
    
          this.results = {}
          this.aggregatedSuggestions = {}
          this.retributeProfiles = {}
    
          this.loadingRetributeProfiles = {}
    
        aggregateSuggestions (results) {
          const aggregated = {}
          Object.keys(results).forEach((account) => {
            let r = results[account]
            if (!r.results || !r.results.accounts) {
    
            } else {
              Object.keys(r.results.accounts).forEach((key) => {
                if (aggregated[key]) {
                  aggregated[key].weight += r.results.accounts[key].weight
                  aggregated[key].accounts.push(account)
                } else {
                  aggregated[key] = {...r.results.accounts[key], accounts: [account], fullId: key}
                }
              })
            }
          })
          return aggregated
        },
    
    Eliot Berriot's avatar
    Eliot Berriot committed
        async fetch (id) {
          let accounts
          if (id) {
            accounts = this.accounts.filter((a) => {
              return a.id == id
            })
          } else {
            accounts = this.accounts
    
            this.retributeProfiles = {}
            this.loadingRetributeProfiles = []
    
          this.isLoadingSources = true
    
    Eliot Berriot's avatar
    Eliot Berriot committed
          accounts.forEach((a) => {
            let r = {isLoading: true, progress: 'indeterminate', status: ''}
    
            let promise = a._source.fetch({account: a, store: this.$store, results: r, vue: this})
            this.$set(this.results, a.id, {account: a, promise, results: r})
          })
          const keys = Object.keys(this.results)
          for(let i = 0; i < keys.length; i++){
            await this.results[keys[i]].promise
          }
    
          this.isLoadingSources = false
    
    Eliot Berriot's avatar
    Eliot Berriot committed
        },
        async lookupAll () {
    
          let self = this
          const toLoad = this.missingRetributeProfiles.filter((p) => {
            return self.loadingRetributeProfiles.indexOf(p.fullId) === -1
          })
    
          if (toLoad.length === 0) {
            return
          }
          this.isLoadingRetribute = true
          const chunkSize = 10
    
          // lock
          toLoad.forEach((p) => {
            self.loadingRetributeProfiles.push(p.fullId)
          })
          const chunks = chunk(toLoad, chunkSize)
    
    Eliot Berriot's avatar
    Eliot Berriot committed
          for (let i = 0; i < chunks.length; i++){
            let chunk = chunks[i]
            let ids = chunk.map((s) => {
              return s.fullId
            })
            await this.lookups(ids)
          }
    
          this.isLoadingRetribute = false
    
    Eliot Berriot's avatar
    Eliot Berriot committed
    
        },
        async lookups(ids) {
    
          let self = this
          ids.forEach((id) => {
            self.loadingRetributeProfiles.push(id)
          })
    
          const client = axios.create()
    
          let url = config.RetributeAPIUrl + `v1/search/`
    
          let response
          try {
            response = await client.post(url, {lookups: ids})
          } catch {
            ids.forEach((id) => {
              self.$set(self.retributeProfiles, id, null)
            })
            return
    
    Eliot Berriot's avatar
    Eliot Berriot committed
          }
    
          ids.forEach((id) => {
            self.$set(self.retributeProfiles, id, response.data[id])
          })
    
    Eliot Berriot's avatar
    Eliot Berriot committed
    
        },
        async lookup (id) {
    
          this.loadingRetributeProfiles.push(id)
    
          const client = axios.create()
    
          let url = config.RetributeAPIUrl + `v1/search/${id}`
    
    Eliot Berriot's avatar
    Eliot Berriot committed
          try {
            const response = await client.get(url)
            this.$set(this.retributeProfiles, id, response.data)
          } catch {
            this.$set(this.retributeProfiles, id, null)
          }
    
          pull(this.loadingRetributeProfiles, [id])
    
      },
      watch: {
    
        async missingRetributeProfiles (v) {
          await this.lookupAll()
        },
    
        results: {
          handler (v) {
            this.aggregatedSuggestions = this.aggregateSuggestions(v)
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            this.$store.commit('setRecursiveState', {key: 'cache.aggregatedSuggestions', value: this.aggregatedSuggestions})
    
          },
          deep: true,
    
    Eliot Berriot's avatar
    Eliot Berriot committed
        },
        retributeProfiles: {
          handler (v) {
            this.$store.commit('setRecursiveState', {key: 'cache.retributeProfiles', value: this.retributeProfiles})
          },
          deep: true