aboutsummaryrefslogtreecommitdiff
path: root/src/hocs/with_subscription/with_subscription.js
blob: b1244276683dd5c88b025ceb13831eefc5451750 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import Vue from 'vue'
import isEmpty from 'lodash/isEmpty'
import { getComponentProps } from '../../services/component_utils/component_utils'
import './with_subscription.scss'

import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
  faCircleNotch
} from '@fortawesome/free-solid-svg-icons'

library.add(
  faCircleNotch
)

const withSubscription = ({
  fetch, // function to fetch entries and return a promise
  select, // function to select data from store
  childPropName = 'content', // name of the prop to be passed into the wrapped component
  additionalPropNames = [] // additional prop name list of the wrapper component
}) => (WrappedComponent) => {
  const originalProps = Object.keys(getComponentProps(WrappedComponent))
  const props = originalProps.filter(v => v !== childPropName).concat(additionalPropNames)

  return Vue.component('withSubscription', {
    props: [
      ...props,
      'refresh' // boolean saying to force-fetch data whenever created
    ],
    data () {
      return {
        loading: false,
        error: false
      }
    },
    computed: {
      fetchedData () {
        return select(this.$props, this.$store)
      }
    },
    created () {
      if (this.refresh || isEmpty(this.fetchedData)) {
        this.fetchData()
      }
    },
    methods: {
      fetchData () {
        if (!this.loading) {
          this.loading = true
          this.error = false
          fetch(this.$props, this.$store)
            .then(() => {
              this.loading = false
            })
            .catch(() => {
              this.error = true
              this.loading = false
            })
        }
      }
    },
    render (h) {
      if (!this.error && !this.loading) {
        const props = {
          props: {
            ...this.$props,
            [childPropName]: this.fetchedData
          },
          on: this.$listeners,
          scopedSlots: this.$scopedSlots
        }
        const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
        return (
          <div class="with-subscription">
            <WrappedComponent {...props}>
              {children}
            </WrappedComponent>
          </div>
        )
      } else {
        return (
          <div class="with-subscription-loading">
            {this.error
              ? <a onClick={this.fetchData} class="alert error">{this.$t('general.generic_error')}</a>
              : <FAIcon spin icon="circle-notch"/>
            }
          </div>
        )
      }
    }
  })
}

export default withSubscription