/**
* @prettier
*/
import React from 'react'
import { Transition } from '@headlessui/react'
import { Cog8ToothIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
function Favicon({ domain, className }) {
return (
{
e.target.onerror = null
e.target.src = '/favicon/sources/placeholder'
}}
referrerPolicy="no-referrer"
className={className}
/>
)
}
export default class SiteSwitcher extends React.Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
this.handleKeydown = this.handleKeydown.bind(this)
this.populateSites = this.populateSites.bind(this)
this.toggle = this.toggle.bind(this)
this.siteSwitcherButton = React.createRef()
this.state = {
open: false,
sites: null,
error: null,
loading: true
}
}
componentDidMount() {
this.populateSites()
this.siteSwitcherButton.current.addEventListener('click', this.toggle)
document.addEventListener('keydown', this.handleKeydown)
document.addEventListener('click', this.handleClick, false)
}
componentWillUnmount() {
this.siteSwitcherButton.current.removeEventListener('click', this.toggle)
document.removeEventListener('keydown', this.handleKeydown)
document.removeEventListener('click', this.handleClick, false)
}
populateSites() {
if (!this.props.loggedIn) return
fetch('/api/sites')
.then((response) => {
if (!response.ok) {
throw response
}
return response.json()
})
.then((sites) =>
this.setState({
loading: false,
sites: sites.data.map((s) => s.domain)
})
)
.catch((e) => this.setState({ loading: false, error: e }))
}
handleClick(e) {
// If this is an interaction with the dropdown menu itself, do nothing.
if (this.dropDownNode && this.dropDownNode.contains(e.target)) return
// If the dropdown is not open, do nothing.
if (!this.state.open) return
// In any other case, close it.
this.setState({ open: false })
}
handleKeydown(e) {
if (!this.props.loggedIn) return
const { site } = this.props
const { sites } = this.state
if (e.target.tagName === 'INPUT') return true
if (
e.ctrlKey ||
e.metaKey ||
e.altKey ||
e.isComposing ||
e.keyCode === 229 ||
!sites
)
return
const siteNum = parseInt(e.key)
if (
1 <= siteNum &&
siteNum <= 9 &&
siteNum <= sites.length &&
sites[siteNum - 1] !== site.domain
) {
window.location = `/${encodeURIComponent(sites[siteNum - 1])}`
}
}
toggle(e) {
/**
* React doesn't seem to prioritise its own events when events are bubbling, and is unable to stop its events from propagating to the document's (root) event listeners which are attached on the DOM.
*
* A simple trick is to hook up our own click event listener via a ref node, which allows React to manage events in this situation better between the two.
*/
e.stopPropagation()
e.preventDefault()
if (!this.props.loggedIn) return
this.setState((prevState) => ({
open: !prevState.open
}))
if (this.props.loggedIn && !this.state.sites) {
this.populateSites()
}
}
renderSiteLink(domain, index) {
const extraClass =
domain === this.props.site.domain
? 'font-medium text-gray-900 dark:text-gray-100 cursor-default font-bold'
: 'hover:bg-gray-100 dark:hover:bg-gray-900 hover:text-gray-900 dark:hover:text-gray-100 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-900 dark:focus:text-gray-100'
const showHotkey = !this.props.loggedIn
return (