Home.vue 12 KB
Newer Older
1
<template>
Agate's avatar
Agate committed
2
  <main class="main pusher page-home" v-title="labels.title">
3
4
5
    <section :class="['ui', 'head', {'with-background': banner}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle">
      <div class="segment-content">
        <h1 class="ui center aligned large header">
6
7
8
          <span
            v-translate="{podName: podName}"
            translate-context="Content/Home/Header"
9
10
            :translate-params="{podName: podName}">
            Welcome to %{ podName }!
11
          </span>
12
13
14
          <div v-if="shortDescription" class="sub header">
            {{ shortDescription }}
          </div>
15
16
        </h1>
      </div>
17
18
    </section>
    <section class="ui vertical stripe segment">
19
20
      <div class="ui stackable grid">
        <div class="ten wide column">
21
          <h2 class="header">
22
            <translate translate-context="Content/Home/Header">About this Funkwhale pod</translate>
23
          </h2>
24
25
26
27
28
29
30
31
32
33
34
          <div class="ui raised segment" id="pod">
            <div class="ui stackable grid">
              <div class="eight wide column">
                <p v-if="!truncatedDescription">
                  <translate translate-context="Content/Home/Paragraph">No description available.</translate>
                </p>
                <template v-if="truncatedDescription || rules">
                  <div v-if="truncatedDescription" v-html="truncatedDescription"></div>
                  <div v-if="truncatedDescription" class="ui hidden divider"></div>
                  <div class="ui relaxed list">
                    <div class="item" v-if="truncatedDescription">
Agate's avatar
Agate committed
35
                      <i class="arrow right icon"></i>
36
37
38
39
40
41
42
                      <div class="content">
                        <router-link class="ui link" :to="{name: 'about'}">
                          <translate translate-context="Content/Home/Link">Learn more</translate>
                        </router-link>
                      </div>
                    </div>
                    <div class="item" v-if="rules">
Agate's avatar
Agate committed
43
                      <i class="book open icon"></i>
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
                      <div class="content">
                        <router-link class="ui link" v-if="rules" :to="{name: 'about', hash: '#rules'}">
                          <translate translate-context="Content/Home/Link">Server rules</translate>
                        </router-link>
                      </div>
                    </div>
                  </div>
                </template>
              </div>
              <div class="eight wide column">
                <template v-if="stats">
                  <h3 class="sub header">
                    <translate translate-context="Content/Home/Header">Statistics</translate>
                  </h3>
                  <p>
Agate's avatar
Agate committed
59
                    <i class="user icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.users" translate-plural="%{ count } active users">%{ count } active user</translate>
60
61
                  </p>
                  <p>
Agate's avatar
Agate committed
62
                    <i class="music icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: parseInt(stats.hours).toLocaleString($store.state.ui.momentLocale)}" :translate-n="parseInt(stats.hours)" translate-plural="%{ count } hours of music">%{ count } hour of music</translate>
63
64
65
66
67
68
69
                  </p>

                </template>
                <template v-if="contactEmail">
                  <h3 class="sub header">
                    <translate translate-context="Content/Home/Header/Name">Contact</translate>
                  </h3>
Agate's avatar
Agate committed
70
                  <i class="at icon"></i>
71
72
73
74
                  <a :href="`mailto:${contactEmail}`">{{ contactEmail }}</a>
                </template>

              </div>
75
76
77
            </div>
          </div>
        </div>
78
79

        <div class="six wide column">
80
          <img class="ui image" src="../assets/network.png" alt=""/>
81
82
        </div>
      </div>
83
84
85
86
87
      <div class="ui hidden divider"></div>
      <div class="ui hidden divider"></div>
      <div class="ui stackable grid">
        <div class="four wide column">
          <h3 class="header">
Eliot Berriot's avatar
Eliot Berriot committed
88
            <translate translate-context="Footer/*/Title/Short">About Funkwhale</translate>
89
90
          </h3>
          <p v-translate translate-context="Content/Home/Paragraph">This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network.</p>
jovuit's avatar
Typo    
jovuit committed
91
          <p v-translate translate-context="Content/Home/Paragraph">Funkwhale is free and developed by a friendly community of volunteers.</p>
92
93
94
95
          <a target="_blank" rel="noopener" href="https://funkwhale.audio">
            <i class="external alternate icon"></i>
            <translate translate-context="Content/Home/Link">Visit funkwhale.audio</translate>
          </a>
96
        </div>
97
98
99
100
        <div class="four wide column">
          <h3 class="header">
            <translate translate-context="Head/Login/Title">Log In</translate>
          </h3>
101
          <login-form button-classes="success" :show-signup="false"></login-form>
102
103
104
105
106
107
108
109
110
111
112
113
114
          <div class="ui hidden clearing divider"></div>
        </div>
        <div class="four wide column">
          <h3 class="header">
            <translate translate-context="*/Signup/Title">Sign up</translate>
          </h3>
          <template v-if="openRegistrations">
            <p>
              <translate translate-context="Content/Home/Paragraph">Sign up now to keep a track of your favorites, create playlists, discover new content and much more!</translate>
            </p>
            <p v-if="defaultUploadQuota">
              <translate translate-context="Content/Home/Paragraph" :translate-params="{quota: humanSize(defaultUploadQuota * 1000 * 1000)}">Users on this pod also get %{ quota } of free storage to upload their own content!</translate>
            </p>
115
            <signup-form button-classes="success" :show-login="false"></signup-form>
116
117
118
119
120
121
122
          </template>
          <div v-else>
            <p translate-context="Content/Home/Paragraph">Registrations are closed on this pod. You can signup on another pod using the link below.</p>
            <a target="_blank" rel="noopener" href="https://funkwhale.audio/#get-started">
              <i class="external alternate icon"></i>
              <translate translate-context="Content/Home/Link">Find another pod</translate>
            </a>
123
124
          </div>
        </div>
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

        <div class="four wide column">
          <h3 class="header">
            <translate translate-context="Content/Home/Header">Useful links</translate>
          </h3>
          <div class="ui relaxed list">
            <div class="item">
              <i class="headphones icon"></i>
              <div class="content">
                <router-link v-if="anonymousCanListen" class="header" to="/library">
                  <translate translate-context="Content/Home/Link">Browse public content</translate>
                </router-link>
                <div class="description">
                  <translate translate-context="Content/Home/Link">Listen to public albums and playlists shared on this pod</translate>
                </div>
              </div>
141
            </div>
142
143
144
145
146
147
148
149
150
151
            <div class="item">
              <i class="mobile alternate icon"></i>
              <div class="content">
                <a class="header" href="https://funkwhale.audio/apps" target="_blank" rel="noopener">
                  <translate translate-context="Content/Home/Link">Mobile apps</translate>
                </a>
                <div class="description">
                  <translate translate-context="Content/Home/Link">Use Funkwhale on other devices with our apps</translate>
                </div>
              </div>
152
            </div>
153
154
155
156
157
158
159
160
161
162
            <div class="item">
              <i class="book icon"></i>
              <div class="content">
                <a class="header" href="https://docs.funkwhale.audio/users/index.html" target="_blank" rel="noopener">
                  <translate translate-context="Content/Home/Link">User guides</translate>
                </a>
                <div class="description">
                  <translate translate-context="Content/Home/Link">Discover everything you need to know about Funkwhale and its features</translate>
                </div>
              </div>
163
164
165
166
            </div>
          </div>
        </div>
      </div>
167
    </section>
168
169
170
171
172
173
174
175
    <section v-if="anonymousCanListen" class="ui vertical stripe segment">
      <album-widget :filters="{playable: true, ordering: '-creation_date'}" :limit="10">
        <template slot="title"><translate translate-context="Content/Home/Title">Recently added albums</translate></template>
        <router-link to="/library">
          <translate translate-context="Content/Home/Link">View more…</translate>
          <div class="ui hidden divider"></div>
        </router-link>
      </album-widget>
176
177
178
179
180
181
      <div class="ui hidden section divider"></div>
      <h3 class="ui header" >
        <translate translate-context="*/*/*">New channels</translate>
      </h3>
      <channels-widget :show-modification-date="true" :limit="10" :filters="{ordering: '-creation_date', external: 'false'}"></channels-widget>

182
    </section>
183
  </main>
184
185
186
</template>

<script>
187
188
189
190
191
import $ from 'jquery'
import _ from '@/lodash'
import {mapState} from 'vuex'
import showdown from 'showdown'
import AlbumWidget from "@/components/audio/album/Widget"
192
import ChannelsWidget from "@/components/audio/ChannelsWidget"
193
194
195
196
import LoginForm from "@/components/auth/LoginForm"
import SignupForm from "@/components/auth/SignupForm"
import {humanSize } from '@/filters'

197
export default {
198
199
  components: {
    AlbumWidget,
200
    ChannelsWidget,
201
202
203
204
    LoginForm,
    SignupForm,
  },
  data () {
205
    return {
206
207
208
      markdown: new showdown.Converter(),
      excerptLength: 2, // html nodes,
      humanSize
209
210
    }
  },
Eliot Berriot's avatar
Eliot Berriot committed
211
  computed: {
212
213
214
    ...mapState({
      nodeinfo: state => state.instance.nodeinfo,
    }),
215
    labels() {
Eliot Berriot's avatar
Eliot Berriot committed
216
      return {
217
        title: this.$pgettext('Head/Home/Title', "Home")
Eliot Berriot's avatar
Eliot Berriot committed
218
      }
219
    },
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    podName() {
      return _.get(this.nodeinfo, 'metadata.nodeName') || "Funkwhale"
    },
    banner () {
      return _.get(this.nodeinfo, 'metadata.banner')
    },
    shortDescription () {
      return _.get(this.nodeinfo, 'metadata.shortDescription')
    },
    longDescription () {
      return _.get(this.nodeinfo, 'metadata.longDescription')
    },
    rules () {
      return _.get(this.nodeinfo, 'metadata.rules')
    },
    truncatedDescription () {
      if (!this.longDescription) {
        return
      }
      let doc = this.markdown.makeHtml(this.longDescription)
      let nodes = $.parseHTML(doc)
      let excerptParts = []
      let handled = 0
      nodes.forEach((n) => {
        let content = n.innerHTML || n.nodeValue
        if (handled < this.excerptLength && content.trim()) {
          excerptParts.push(n)
          handled += 1
        }
      })
      return excerptParts.map((p) => { return p.outerHTML }).join('')
    },
    stats () {
      let data = {
        users: _.get(this.nodeinfo, 'usage.users.activeMonth', null),
        hours: _.get(this.nodeinfo, 'metadata.library.music.hours', null),
      }
      if (data.users === null || data.artists === null) {
        return
      }
      return data
    },
    contactEmail () {
      return _.get(this.nodeinfo, 'metadata.contactEmail')
    },
    defaultUploadQuota () {
      return _.get(this.nodeinfo, 'metadata.defaultUploadQuota')
    },
    anonymousCanListen () {
      return _.get(this.nodeinfo, 'metadata.library.anonymousCanListen')
    },
    openRegistrations () {
      return _.get(this.nodeinfo, 'openRegistrations')
    },
    headerStyle() {
      if (!this.banner) {
        return ""
      }
      return (
        "background-image: url(" +
        this.$store.getters["instance/absoluteUrl"](this.banner) +
        ")"
      )
    },
284
285
286
287
288
289
290
291
292
293
294
  },
  watch: {
    '$store.state.auth.authenticated': {
      handler (v) {
        if (v) {
          console.log('Authenticated, redirecting to /library…')
          this.$router.push('/library')
        }
      },
      immediate: true
    }
295
  }
296

297
298
}
</script>