Skip to content

Commit

Permalink
Merge pull request #170 from radio4000/feat/r4-user-channels-select
Browse files Browse the repository at this point in the history
re-add and fix the r4-user-channels-select
  • Loading branch information
4www committed May 16, 2024
2 parents 0fba16c + 5cd2de6 commit 01ebc69
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 157 deletions.
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import R4Actions from './r4-actions.js'
import R4Admin from './r4-admin.js'
import R4App from './r4-app.js'
import R4AppMenu from './r4-app-menu.js'
import R4AppUserMenu from './r4-app-user-menu.js'
import R4CommandMenu from './r4-command-menu.js'
import R4Avatar from './r4-avatar.js'
import R4AvatarUpdate from './r4-avatar-update.js'
Expand Down Expand Up @@ -57,6 +58,7 @@ const componentDefinitions = {
'r4-app': R4App,
'r4-command-menu': R4CommandMenu,
'r4-app-menu': R4AppMenu,
'r4-app-user-menu': R4AppUserMenu,
'r4-avatar': R4Avatar,
'r4-avatar-update': R4AvatarUpdate,
'r4-avatar-upload': R4AvatarUpload,
Expand Down
35 changes: 4 additions & 31 deletions src/components/r4-app-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,34 @@ import {LitElement, html, nothing} from 'lit'
* The primary menu for <r4-app>
*/
export default class R4AppMenu extends LitElement {
createRenderRoot() {
return this
}
static properties = {
href: {type: String},
slug: {type: String},
/** If set to true, renders different menu items */
auth: {type: Boolean},
/** Used to set active link */
path: {type: Boolean, state: true},
path: {type: String, state: true},
}

constructor() {
super()
// Set "path" on every navigation.
window?.navigation?.addEventListener('navigate', (e) => {
this.path = e.destination.url.replace(this.href, '').split('?')[0]
})
}

get canAdd() {
/* could just be "slug", menu has slug if "user" has channel */
return this.auth && this.slug
}

isCurrent(path) {
return this.path === path ? 'page' : nothing
}

render() {
const {href, path} = this
return html`
<menu>
<li>
<a aria-current=${this.isCurrent('/')} href=${href + '/'}><r4-title size="small"></r4-title></a>
</li>
<li>${this.auth ? this.renderAuth() : this.renderNoAuth()}</li>
<li>${this.canAdd ? this.renderAdd() : null}</li>
<li><a aria-current=${this.isCurrent('/explore')} href=${href + '/explore'}>Explore</a></li>
<li><a aria-current=${this.isCurrent('/map')} href=${href + '/map'}>Map</a></li>
<li><a aria-current=${this.isCurrent('/settings')} href=${href + '/settings'}>Settings</a></li>
</menu>
`
}
renderNoAuth() {}
renderAuth() {
if (this.slug) {
return html`
<a aria-current=${this.isCurrent(`/${this.slug}`)} href=${this.href + '/' + this.slug}>@${this.slug}</a>
`
} else {
return html`<a aria-current=${this.isCurrent('/new')} href=${this.href + '/new'}>New radio</a>`
}
}
renderAdd() {
return html`<a aria-current=${this.isCurrent('/add')} href=${this.href + '/add?slug=' + this.slug}>Add</a>`
}
createRenderRoot() {
return this
}
}
64 changes: 64 additions & 0 deletions src/components/r4-app-user-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {LitElement, html, nothing} from 'lit'

/**
* The primary menu for a User of the <r4-app>
*/
export default class R4AppUserMenu extends LitElement {
createRenderRoot() {
return this
}
static properties = {
href: {type: String},
channel: {type: Object || null},
channels: {type: Array || null},
}
constructor() {
super()
window?.navigation?.addEventListener('navigate', (e) => {
this.path = e.destination.url.replace(this.href, '').split('?')[0]
})
}

isCurrent(path) {
return this.path === path ? 'page' : nothing
}
onChannelSelect(event) {
event.stopPropagation()
event.preventDefault()
this.dispatchEvent(
new CustomEvent('select', {
bubbles: true,
detail: event.detail,
}),
)
}

render() {
console.log('channels', this.channels, this.channel)
const {href, path} = this
return html`
<menu>
<li>${this.renderChannel()}</li>
<li>${this.channel ? this.renderAdd() : null}</li>
</menu>
`
}
renderChannel() {
if (this.channels) {
return html`<r4-user-channels-select
.channels=${this.channels}
.channel=${this.channel}
@select=${this.onChannelSelect}
></r4-user-channels-select>`
} else if (this.channel) {
return html`<a aria-current=${this.isCurrent(`/${this.channel.slug}`)} href=${this.href + '/' + this.channel.slug}
>@${this.channel.slug}</a
>`
} else {
return html`<a aria-current=${this.isCurrent('/new')} href=${this.href + '/new'}>New radio</a>`
}
}
renderAdd() {
return html`<a aria-current=${this.isCurrent('/add')} href=${this.href + '/add?slug=' + this.channel.slug}>Add</a>`
}
}
36 changes: 21 additions & 15 deletions src/components/r4-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ export default class R4App extends LitElement {
this.followers = []
this.following = []
} else {

// Handle case after deleting user
const {error: userError} = await sdk.users.readUser()
if (userError) {
Expand Down Expand Up @@ -233,11 +232,11 @@ export default class R4App extends LitElement {
}
}

onChannelSelect({detail}) {
if (detail.channel) {
const {slug} = detail.channel
onChannelSelect({detail: channel}) {
if (channel) {
const {slug} = channel
this.selectedSlug = slug
page(`/${this.selectedSlug}`)
/* page(`/${this.selectedSlug}`) */
}
}

Expand All @@ -258,10 +257,9 @@ export default class R4App extends LitElement {
// Load and queue them in player
const slug = channel?.slug || track.slug
if (slug && !tracks?.length) {

// Don't load and set tracks if we can avoid it.
const sameName = el.playlist && el.playlist.title === channel?.name
if (sameName && el.tracks?.length ) {
const sameName = el.playlist && el.playlist.title === channel?.name
if (sameName && el.tracks?.length) {
// if (!el.track) {
// const randomTrack = el.tracks[Math.floor(Math.random() * el.tracks.length)]
// el.track = randomTrack
Expand All @@ -276,10 +274,13 @@ export default class R4App extends LitElement {
el.track = track || tracks.at(-1)
}
} else if (slug && tracks) {
el.tracks = tracks.slice().reverse().map((track) => {
track.body = track.description
return track
})
el.tracks = tracks
.slice()
.reverse()
.map((track) => {
track.body = track.description
return track
})
el.track = tracks.at(0)
}

Expand All @@ -302,7 +303,6 @@ export default class R4App extends LitElement {
el.removeAttribute('image')
}


// Update state about what's playing.
this.isPlaying = true
this.playingChannel = channel
Expand Down Expand Up @@ -338,10 +338,10 @@ export default class R4App extends LitElement {
<main slot="main">${this.renderRouter()}</main>
<r4-player
slot="player"
${ref(this.playerRef)}
platform="true"
.isPlaying=${this.config.isPlaying}
@trackchange=${this.onTrackChange}
${ref(this.playerRef)}
></r4-player>
<form slot="playback-controls">${Object.entries(UI_STATES).map(this.renderPlayerUICtrl.bind(this))}</form>
</r4-layout>
Expand Down Expand Up @@ -369,7 +369,13 @@ export default class R4App extends LitElement {
renderMenu() {
return html`
<header slot="menu">
<r4-app-menu ?auth=${this.store?.user} href=${this.config?.href} slug=${this.selectedSlug}></r4-app-menu>
<r4-app-menu ?auth=${this.store?.user} href=${this.config?.href}></r4-app-menu>
<r4-app-user-menu
href=${this.config?.href}
.channel=${this.store.selectedChannel}
.channels=${this.store.userChannels}
@select=${this.onChannelSelect}
></r4-app-user-menu>
</header>
`
}
Expand Down
141 changes: 30 additions & 111 deletions src/components/r4-user-channels-select.js
Original file line number Diff line number Diff line change
@@ -1,121 +1,40 @@
import {LitElement, html} from 'lit'
import {sdk} from '../libs/sdk.js'

export default class R4UserChannelsSelect extends HTMLElement {
static get observedAttributes() {
return ['channels', 'channel']
export default class R4UserChannelsSelect extends LitElement {
createRenderRoot() {
return this
}

get channel() {
return this.getAttribute('channel')
}
set channel(str) {
this.setAttribute('channel', str)
}

get channels() {
return JSON.parse(this.getAttribute('channels')) || []
}
set channels(obj) {
if (obj) {
this.setAttribute('channels', JSON.stringify(obj))
} else {
this.removeAttribute('channels')
}
}

/* if any observed attribute changed, re-render */
attributeChangedCallback(attrName, newVal) {
if (this.constructor.observedAttributes.indexOf(attrName) > -1) {
this.render()
if (attrName === 'channel') {
this.refreshOptions(newVal)
}
}
static properties = {
channel: {type: Object || null},
channels: {type: Array || null},
}

connectedCallback() {
sdk.supabase.auth.onAuthStateChange(this.onAuthStateChange.bind(this))

this.$select = document.createElement('select')
this.$select.addEventListener('input', this.onInput.bind(this))

this.$defaultOptroup = document.createElement('optgroup')
this.$defaultOptroup.label = 'Selected'

this.$defaultOption = document.createElement('option')
this.$defaultOption.defaultValue = true
this.$defaultOption.value = ''
this.$defaultOption.innerText = this.channel ? this.channel : '…'

this.$defaultOptroup.append(this.$defaultOption)

this.$channelsOptgroup = document.createElement('optgroup')
this.$channelsOptgroup.label = 'All'
this.$select.append(this.$defaultOptroup)
this.$select.append(this.$channelsOptgroup)

this.append(this.$select)

if (this.channels && this.channels.length) {
this.refreshOptions(this.channels[0].slug)
} else {
this.refreshUserChannels()
}
}
onInput(event) {
onSelect(event) {
event.stopPropagation()
/* event.preventDefault() */
this.channel = event.target.value
const selectedChannel = this.channels.find((channel) => {
return channel.slug === this.channel
})
const inputEvent = new CustomEvent('input', {
bubbles: true,
detail: {
channels: this.channels,
channel: selectedChannel,
}
})
this.dispatchEvent(inputEvent)
this.refreshOptions(this.channel)
}

onAuthStateChange() {
this.refreshUserChannels()
}

async refreshUserChannels() {
const { data: user } = await sdk.users.readUser()
if (user) {
const {
error,
data,
} = await sdk.channels.readUserChannels()

this.error = error
this.channels = data
if (this.channels && this.channels.length) {
if (this.channel) {
this.refreshOptions(this.channel)
} else {
this.refreshOptions(this.channels[0].slug)
}
}
} else {
this.channels = []
}
event.preventDefault()
const {value: id} = event.target
this.dispatchEvent(
new CustomEvent('select', {
bubbles: true,
detail: this.channels.find((c) => c.id === id),
}),
)
}

render() {
this.$channelsOptgroup.innerHTML = ''
this.channels.map((channel) => {
const $option = document.createElement('option')
$option.value = channel.slug
$option.innerText = channel.slug
this.$channelsOptgroup.append($option)
return html`
<label>
<select @change=${this.onSelect} title="Selected Radio">
<optgroup label="Selected">
<option>${this.channel.slug}</option>
</optgroup>
<optgroup label="Channels">${this._renderOptions()}</optgroup>
</select>
</label>
`
}
_renderOptions() {
return this.channels?.map((channel) => {
return html` <option value=${channel.id}>${channel.slug}</option> `
})
}
refreshOptions(channelSlug) {
this.$defaultOption.innerText = channelSlug
}
}

0 comments on commit 01ebc69

Please sign in to comment.