Commit 8700e1b6 authored by Agate's avatar Agate 💬

Merge branch 'listening-start' into 'develop'

Fix #1060: Added a new radio based on another user listenings

Closes #1060

See merge request !1067
parents 464010f0 cd63646f
Pipeline #10086 passed with stages
in 9 minutes and 45 seconds
Added a new radio based on another user listenings (#1060)
...@@ -42,6 +42,7 @@ import { WebSocketBridge } from 'django-channels' ...@@ -42,6 +42,7 @@ import { WebSocketBridge } from 'django-channels'
import GlobalEvents from '@/components/utils/global-events' import GlobalEvents from '@/components/utils/global-events'
import moment from 'moment' import moment from 'moment'
import locales from './locales' import locales from './locales'
import {getClientOnlyRadio} from '@/radios'
export default { export default {
name: 'app', name: 'app',
...@@ -138,6 +139,11 @@ export default { ...@@ -138,6 +139,11 @@ export default {
id: 'sidebarPendingReviewRequestCount', id: 'sidebarPendingReviewRequestCount',
handler: this.incrementPendingReviewRequestsCountInSidebar handler: this.incrementPendingReviewRequestsCountInSidebar
}) })
this.$store.commit('ui/addWebsocketEventHandler', {
eventName: 'Listen',
id: 'handleListen',
handler: this.handleListen
})
}, },
mounted () { mounted () {
let self = this let self = this
...@@ -175,6 +181,10 @@ export default { ...@@ -175,6 +181,10 @@ export default {
eventName: 'user_request.created', eventName: 'user_request.created',
id: 'sidebarPendingReviewRequestCount', id: 'sidebarPendingReviewRequestCount',
}) })
this.$store.commit('ui/removeWebsocketEventHandler', {
eventName: 'Listen',
id: 'handleListen',
})
this.disconnect() this.disconnect()
}, },
methods: { methods: {
...@@ -190,6 +200,14 @@ export default { ...@@ -190,6 +200,14 @@ export default {
incrementPendingReviewRequestsCountInSidebar (event) { incrementPendingReviewRequestsCountInSidebar (event) {
this.$store.commit('ui/incrementNotifications', {type: 'pendingReviewRequests', value: event.pending_count}) this.$store.commit('ui/incrementNotifications', {type: 'pendingReviewRequests', value: event.pending_count})
}, },
handleListen (event) {
if (this.$store.state.radios.current && this.$store.state.radios.running) {
let current = this.$store.state.radios.current
if (current.clientOnly && current.type === 'account') {
getClientOnlyRadio(current).handleListen(current, event, this.$store)
}
}
},
async fetchNodeInfo () { async fetchNodeInfo () {
let response = await axios.get('instance/nodeinfo/2.0/') let response = await axios.get('instance/nodeinfo/2.0/')
this.$store.commit('instance/nodeinfo', response.data) this.$store.commit('instance/nodeinfo', response.data)
......
...@@ -251,6 +251,8 @@ export default { ...@@ -251,6 +251,8 @@ export default {
progressInterval: null, progressInterval: null,
maxPreloaded: 3, maxPreloaded: 3,
preloadDelay: 15, preloadDelay: 15,
listenDelay: 15,
listeningRecorded: null,
soundsCache: [], soundsCache: [],
soundId: null, soundId: null,
playTimeout: null, playTimeout: null,
...@@ -477,6 +479,13 @@ export default { ...@@ -477,6 +479,13 @@ export default {
this.getSound(toPreload) this.getSound(toPreload)
this.nextTrackPreloaded = true this.nextTrackPreloaded = true
} }
if (t > this.listenDelay || d - t < 30) {
let onlyTrack = this.$store.state.queue.tracks.length === 1
if (this.listeningRecorded != this.currentTrack) {
this.listeningRecorded = this.currentTrack
this.$store.dispatch('player/trackListened', this.currentTrack)
}
}
} }
}, },
seek (step) { seek (step) {
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
<tags-list label-classes="tiny" :truncate-size="20" :limit="2" :show-more="false" :tags="object.track.tags"></tags-list> <tags-list label-classes="tiny" :truncate-size="20" :limit="2" :show-more="false" :tags="object.track.tags"></tags-list>
<div class="extra" v-if="isActivity"> <div class="extra" v-if="isActivity">
<span class="left floated">@{{ object.user.username }}</span> <router-link class="left floated" :to="{name: 'profile.overview', params: {username: object.user.username}}">@{{ object.user.username }}</router-link>
<span class="right floated"><human-date :date="object.creation_date" /></span> <span class="right floated"><human-date :date="object.creation_date" /></span>
</div> </div>
</div> </div>
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
<script> <script>
import lodash from '@/lodash'
export default { export default {
props: { props: {
customRadioId: {required: false}, customRadioId: {required: false},
type: {type: String, required: false}, type: {type: String, required: false},
clientOnly: {type: Boolean, default: false},
objectId: {default: null} objectId: {default: null}
}, },
methods: { methods: {
...@@ -19,7 +21,12 @@ export default { ...@@ -19,7 +21,12 @@ export default {
if (this.running) { if (this.running) {
this.$store.dispatch('radios/stop') this.$store.dispatch('radios/stop')
} else { } else {
this.$store.dispatch('radios/start', {type: this.type, objectId: this.objectId, customRadioId: this.customRadioId}) this.$store.dispatch('radios/start', {
type: this.type,
objectId: this.objectId,
customRadioId: this.customRadioId,
clientOnly: this.clientOnly,
})
} }
} }
}, },
...@@ -30,7 +37,7 @@ export default { ...@@ -30,7 +37,7 @@ export default {
if (!state.running) { if (!state.running) {
return false return false
} else { } else {
return current.type === this.type && current.objectId === this.objectId && current.customRadioId === this.customRadioId return current.type === this.type && lodash.isEqual(current.objectId, this.objectId) && current.customRadioId === this.customRadioId
} }
} }
} }
......
import axios from "axios"
import logger from '@/logging'
// import axios from 'axios'
const RADIOS = {
// some radios are client side only, so we have to implement the populateQueue
// method by hand
account: {
offset: 1,
populateQueue({current, dispatch, playNow}) {
let params = {scope: `actor:${current.objectId.fullUsername}`, ordering: '-creation_date', page_size: 1, page: this.offset}
axios.get('history/listenings', {params}).then((response) => {
let latest = response.data.results[0]
if (!latest) {
logger.default.error('No more tracks')
dispatch('stop')
}
this.offset += 1
let append = dispatch('queue/append', {track: latest.track}, {root: true})
if (playNow) {
append.then(() => {
dispatch('queue/last', null, {root: true})
})
}
}, (error) => {
logger.default.error('Error while fetching listenings', error)
dispatch('stop')
})
},
stop () {
this.offset = 1
},
handleListen (current, event, store) {
// XXX: handle actors from other pods
if (event.actor.local_id === current.objectId.username) {
axios.get(`tracks/${event.object.local_id}`).then((response) => {
if (response.data.uploads.length > 0) {
store.dispatch('queue/append', {track: response.data, index: store.state.queue.currentIndex + 1})
this.offset += 1
}
}, (error) => {
logger.default.error('Cannot retrieve track info', error)
})
}
}
}
}
export function getClientOnlyRadio({type}) {
return RADIOS[type]
}
...@@ -127,7 +127,6 @@ export default { ...@@ -127,7 +127,6 @@ export default {
}) })
}, },
trackEnded ({dispatch, rootState}, track) { trackEnded ({dispatch, rootState}, track) {
dispatch('trackListened', track)
let queueState = rootState.queue let queueState = rootState.queue
if (queueState.currentIndex === queueState.tracks.length - 1) { if (queueState.currentIndex === queueState.tracks.length - 1) {
// we've reached last track of queue, trigger a reload // we've reached last track of queue, trigger a reload
......
import axios from 'axios' import axios from 'axios'
import logger from '@/logging' import logger from '@/logging'
import {getClientOnlyRadio} from '@/radios'
export default { export default {
namespaced: true, namespaced: true,
state: { state: {
...@@ -42,11 +44,17 @@ export default { ...@@ -42,11 +44,17 @@ export default {
} }
}, },
actions: { actions: {
start ({commit, dispatch}, {type, objectId, customRadioId}) { start ({commit, dispatch}, {type, objectId, customRadioId, clientOnly}) {
var params = { var params = {
radio_type: type, radio_type: type,
related_object_id: objectId, related_object_id: objectId,
custom_radio: customRadioId custom_radio: customRadioId,
}
if (clientOnly) {
commit('current', {type, objectId, customRadioId, clientOnly})
commit('running', true)
dispatch('populateQueue', true)
return
} }
return axios.post('radios/sessions/', params).then((response) => { return axios.post('radios/sessions/', params).then((response) => {
logger.default.info('Successfully started radio ', type) logger.default.info('Successfully started radio ', type)
...@@ -57,7 +65,10 @@ export default { ...@@ -57,7 +65,10 @@ export default {
logger.default.error('Error while starting radio', type) logger.default.error('Error while starting radio', type)
}) })
}, },
stop ({commit}) { stop ({commit, state}) {
if (state.current && state.current.clientOnly) {
getClientOnlyRadio(state.current).stop()
}
commit('current', null) commit('current', null)
commit('running', false) commit('running', false)
}, },
...@@ -71,6 +82,9 @@ export default { ...@@ -71,6 +82,9 @@ export default {
var params = { var params = {
session: state.current.session session: state.current.session
} }
if (state.current.clientOnly) {
return getClientOnlyRadio(state.current).populateQueue({current: state.current, dispatch, state, rootState, playNow})
}
return axios.post('radios/tracks/', params).then((response) => { return axios.post('radios/tracks/', params).then((response) => {
logger.default.info('Adding track to queue from radio') logger.default.info('Adding track to queue from radio')
let append = dispatch('queue/append', {track: response.data.track}, {root: true}) let append = dispatch('queue/append', {track: response.data.track}, {root: true})
......
...@@ -31,6 +31,7 @@ export default { ...@@ -31,6 +31,7 @@ export default {
'mutation.updated': {}, 'mutation.updated': {},
'report.created': {}, 'report.created': {},
'user_request.created': {}, 'user_request.created': {},
'Listen': {},
}, },
pageTitle: null, pageTitle: null,
routePreferences: { routePreferences: {
......
...@@ -43,6 +43,9 @@ ...@@ -43,6 +43,9 @@
</div> </div>
</template> </template>
</h1> </h1>
<div class="ui center aligned text">
<radio-button type="account" :object-id="{username: object.preferred_username, fullUsername: object.full_username}" :client-only="true"></radio-button>
</div>
<div class="ui small hidden divider"></div> <div class="ui small hidden divider"></div>
<div v-if="$store.getters['ui/layoutVersion'] === 'large'"> <div v-if="$store.getters['ui/layoutVersion'] === 'large'">
<rendered-description <rendered-description
...@@ -82,6 +85,7 @@ import { mapState } from "vuex" ...@@ -82,6 +85,7 @@ import { mapState } from "vuex"
import axios from 'axios' import axios from 'axios'
import ReportMixin from '@/components/mixins/Report' import ReportMixin from '@/components/mixins/Report'
import RadioButton from "@/components/radios/Button"
export default { export default {
mixins: [ReportMixin], mixins: [ReportMixin],
...@@ -89,6 +93,9 @@ export default { ...@@ -89,6 +93,9 @@ export default {
username: {type: String, required: true}, username: {type: String, required: true},
domain: {type: String, required: false, default: null}, domain: {type: String, required: false, default: null},
}, },
components: {
RadioButton,
},
data () { data () {
return { return {
object: null, object: null,
......
...@@ -136,7 +136,6 @@ describe('store/player', () => { ...@@ -136,7 +136,6 @@ describe('store/player', () => {
payload: {test: 'track'}, payload: {test: 'track'},
params: {rootState: {queue: {currentIndex: 0, tracks: [1, 2]}}}, params: {rootState: {queue: {currentIndex: 0, tracks: [1, 2]}}},
expectedActions: [ expectedActions: [
{ type: 'trackListened', payload: {test: 'track'} },
{ type: 'queue/next', payload: null, options: {root: true} } { type: 'queue/next', payload: null, options: {root: true} }
] ]
}) })
...@@ -147,7 +146,6 @@ describe('store/player', () => { ...@@ -147,7 +146,6 @@ describe('store/player', () => {
payload: {test: 'track'}, payload: {test: 'track'},
params: {rootState: {queue: {currentIndex: 1, tracks: [1, 2]}}}, params: {rootState: {queue: {currentIndex: 1, tracks: [1, 2]}}},
expectedActions: [ expectedActions: [
{ type: 'trackListened', payload: {test: 'track'} },
{ type: 'radios/populateQueue', payload: null, options: {root: true} }, { type: 'radios/populateQueue', payload: null, options: {root: true} },
{ type: 'queue/next', payload: null, options: {root: true} } { type: 'queue/next', payload: null, options: {root: true} }
] ]
......
...@@ -58,6 +58,7 @@ describe('store/radios', () => { ...@@ -58,6 +58,7 @@ describe('store/radios', () => {
it('stop', () => { it('stop', () => {
return testAction({ return testAction({
action: store.actions.stop, action: store.actions.stop,
params: {state: {}},
expectedMutations: [ expectedMutations: [
{ type: 'current', payload: null }, { type: 'current', payload: null },
{ type: 'running', payload: false } { type: 'running', payload: false }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment