feat: bouton Répondre via Mastodon (authorize_interaction)
This commit is contained in:
@@ -81,6 +81,11 @@ NEXT_PUBLIC_GIT=
|
|||||||
NEXT_PUBLIC_CODEBERG=
|
NEXT_PUBLIC_CODEBERG=
|
||||||
NEXT_PUBLIC_BLUESKY_URL=
|
NEXT_PUBLIC_BLUESKY_URL=
|
||||||
|
|
||||||
|
# Commentaires fédérés (bokante.o-k-i.net)
|
||||||
|
|
||||||
|
NEXT_PUBLIC_BOKANTE_URL=https://bokante.o-k-i.net
|
||||||
|
NEXT_PUBLIC_BOKANTE_ACCOUNT=pawol_nu
|
||||||
|
|
||||||
# DOMAIN IMAGE
|
# DOMAIN IMAGE
|
||||||
NEXT_PUBLIC_DOMAINS_IMAGE="localhost:1337 strapi.mondomaine.com"
|
NEXT_PUBLIC_DOMAINS_IMAGE="localhost:1337 strapi.mondomaine.com"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import {describe, it, expect, vi, afterEach} from 'vitest'
|
||||||
|
import {render, screen} from '@testing-library/react'
|
||||||
|
import userEvent from '@testing-library/user-event'
|
||||||
|
import ReponsMastodon from '../repons-mastodon'
|
||||||
|
|
||||||
|
describe('ReponsMastodon', () => {
|
||||||
|
const originalOpen = window.open
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.open = originalOpen
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ne rend rien sans bokanteStatusId', () => {
|
||||||
|
const {container} = render(<ReponsMastodon bokanteStatusId={null} />)
|
||||||
|
expect(container).toBeEmptyDOMElement()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('affiche un lien direct vers le fil bokante', () => {
|
||||||
|
render(<ReponsMastodon bokanteStatusId='112233' />)
|
||||||
|
const lien = screen.getByRole('link', {name: 'Mastodon'})
|
||||||
|
expect(lien).toHaveAttribute('href', 'https://bokante.o-k-i.net/@pawol_nu/112233')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('redirige vers authorize_interaction avec le domaine extrait d\'un identifiant complet', async () => {
|
||||||
|
window.open = vi.fn()
|
||||||
|
const user = userEvent.setup()
|
||||||
|
render(<ReponsMastodon bokanteStatusId='112233' />)
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('button', {name: /répondre via mastodon/i}))
|
||||||
|
await user.type(screen.getByLabelText(/@vous@votre-instance/i), '@quelqun@mastodon.social')
|
||||||
|
await user.click(screen.getByRole('button', {name: /continuer/i}))
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(
|
||||||
|
'https://mastodon.social/authorize_interaction?uri=' + encodeURIComponent('https://bokante.o-k-i.net/@pawol_nu/112233'),
|
||||||
|
'_blank',
|
||||||
|
'noopener,noreferrer'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('accepte aussi juste un nom d\'instance sans @', async () => {
|
||||||
|
window.open = vi.fn()
|
||||||
|
const user = userEvent.setup()
|
||||||
|
render(<ReponsMastodon bokanteStatusId='112233' />)
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('button', {name: /répondre via mastodon/i}))
|
||||||
|
await user.type(screen.getByLabelText(/@vous@votre-instance/i), 'mastodon.social')
|
||||||
|
await user.click(screen.getByRole('button', {name: /continuer/i}))
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('https://mastodon.social/authorize_interaction'),
|
||||||
|
'_blank',
|
||||||
|
'noopener,noreferrer'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import {useState} from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import Dialog from '@mui/material/Dialog'
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle'
|
||||||
|
import DialogContent from '@mui/material/DialogContent'
|
||||||
|
import DialogActions from '@mui/material/DialogActions'
|
||||||
|
import TextField from '@mui/material/TextField'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
const BOKANTE_URL = process.env.NEXT_PUBLIC_BOKANTE_URL || 'https://bokante.o-k-i.net'
|
||||||
|
const BOKANTE_ACCOUNT = process.env.NEXT_PUBLIC_BOKANTE_ACCOUNT || 'pawol_nu'
|
||||||
|
|
||||||
|
function extraireInstance(saisie) {
|
||||||
|
const valeur = saisie.trim().replace(/^@/, '').replace(/^https?:\/\//, '').replace(/\/$/, '')
|
||||||
|
return valeur.includes('@') ? valeur.split('@').pop() : valeur
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ReponsMastodon({bokanteStatusId}) {
|
||||||
|
const [ouvert, setOuvert] = useState(false)
|
||||||
|
const [saisie, setSaisie] = useState('')
|
||||||
|
|
||||||
|
if (!bokanteStatusId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const tootUrl = `${BOKANTE_URL}/@${BOKANTE_ACCOUNT}/${bokanteStatusId}`
|
||||||
|
|
||||||
|
const repondre = () => {
|
||||||
|
const instance = extraireInstance(saisie)
|
||||||
|
if (!instance) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(`https://${instance}/authorize_interaction?uri=${encodeURIComponent(tootUrl)}`, '_blank', 'noopener,noreferrer')
|
||||||
|
setOuvert(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{maxWidth: 700, margin: '1em auto 0', textAlign: 'center'}}>
|
||||||
|
<Typography variant='body2' sx={{mb: 1}}>
|
||||||
|
Rejoignez la discussion sur{' '}
|
||||||
|
<Link href={tootUrl} target='_blank' rel='noopener noreferrer'>Mastodon</Link>
|
||||||
|
</Typography>
|
||||||
|
<Button variant='outlined' size='small' onClick={() => setOuvert(true)}>
|
||||||
|
Répondre via Mastodon
|
||||||
|
</Button>
|
||||||
|
<Dialog open={ouvert} onClose={() => setOuvert(false)}>
|
||||||
|
<DialogTitle>Répondre depuis votre compte Mastodon</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Typography variant='body2' sx={{mb: 2}}>
|
||||||
|
Indiquez votre identifiant (@vous@votre-instance) ou juste le nom de votre
|
||||||
|
instance, vous serez redirigé·e vers votre compte pour répondre.
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
fullWidth
|
||||||
|
label='@vous@votre-instance'
|
||||||
|
value={saisie}
|
||||||
|
onChange={event => setSaisie(event.target.value)}
|
||||||
|
onKeyDown={event => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
repondre()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => setOuvert(false)}>Annuler</Button>
|
||||||
|
<Button variant='contained' onClick={repondre}>Continuer</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReponsMastodon.propTypes = {
|
||||||
|
bokanteStatusId: PropTypes.string
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ import EntegreMizik from './entegre-mizik'
|
|||||||
import OkiMizik from './oki-mizik'
|
import OkiMizik from './oki-mizik'
|
||||||
import DiferansDialog from './diferans-dialog'
|
import DiferansDialog from './diferans-dialog'
|
||||||
import Komante from './komante'
|
import Komante from './komante'
|
||||||
|
import ReponsMastodon from './repons-mastodon'
|
||||||
import {useCurrentTrack} from './current-track-context'
|
import {useCurrentTrack} from './current-track-context'
|
||||||
|
|
||||||
const IMAGE_URL = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337'
|
const IMAGE_URL = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337'
|
||||||
@@ -282,6 +283,7 @@ export default function Teks({parole}) {
|
|||||||
)
|
)
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
<ReponsMastodon bokanteStatusId={parole.bokanteStatusId} />
|
||||||
<Komante commentaires={parole.commentaires} />
|
<Komante commentaires={parole.commentaires} />
|
||||||
</Root>
|
</Root>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user