From 21248b71383c8a903b2da4630fb1c490c706cee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Sun, 5 Jul 2026 01:15:55 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20bouton=20R=C3=A9pondre=20via=20Mastodon?= =?UTF-8?q?=20(authorize=5Finteraction)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.sample | 5 ++ .../teks/__tests__/repons-mastodon.test.jsx | 55 ++++++++++++ components/teks/repons-mastodon.jsx | 83 +++++++++++++++++++ components/teks/teks.js | 2 + 4 files changed, 145 insertions(+) create mode 100644 components/teks/__tests__/repons-mastodon.test.jsx create mode 100644 components/teks/repons-mastodon.jsx diff --git a/.env.sample b/.env.sample index 114d967..65f689a 100644 --- a/.env.sample +++ b/.env.sample @@ -81,6 +81,11 @@ NEXT_PUBLIC_GIT= NEXT_PUBLIC_CODEBERG= 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 NEXT_PUBLIC_DOMAINS_IMAGE="localhost:1337 strapi.mondomaine.com" diff --git a/components/teks/__tests__/repons-mastodon.test.jsx b/components/teks/__tests__/repons-mastodon.test.jsx new file mode 100644 index 0000000..afe6b19 --- /dev/null +++ b/components/teks/__tests__/repons-mastodon.test.jsx @@ -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() + expect(container).toBeEmptyDOMElement() + }) + + it('affiche un lien direct vers le fil bokante', () => { + render() + 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() + + 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() + + 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' + ) + }) +}) diff --git a/components/teks/repons-mastodon.jsx b/components/teks/repons-mastodon.jsx new file mode 100644 index 0000000..fd63b63 --- /dev/null +++ b/components/teks/repons-mastodon.jsx @@ -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 ( + + + Rejoignez la discussion sur{' '} + Mastodon + + + setOuvert(false)}> + Répondre depuis votre compte Mastodon + + + Indiquez votre identifiant (@vous@votre-instance) ou juste le nom de votre + instance, vous serez redirigé·e vers votre compte pour répondre. + + setSaisie(event.target.value)} + onKeyDown={event => { + if (event.key === 'Enter') { + repondre() + } + }} + /> + + + + + + + + ) +} + +ReponsMastodon.propTypes = { + bokanteStatusId: PropTypes.string +} diff --git a/components/teks/teks.js b/components/teks/teks.js index 2c15136..d3a9d00 100644 --- a/components/teks/teks.js +++ b/components/teks/teks.js @@ -30,6 +30,7 @@ import EntegreMizik from './entegre-mizik' import OkiMizik from './oki-mizik' import DiferansDialog from './diferans-dialog' import Komante from './komante' +import ReponsMastodon from './repons-mastodon' import {useCurrentTrack} from './current-track-context' const IMAGE_URL = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337' @@ -282,6 +283,7 @@ export default function Teks({parole}) { ) ))} + )